2006-02-15 17:34:47 +00:00
|
|
|
/** Implementation for NSStream for GNUStep
|
|
|
|
Copyright (C) 2006 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
Written by: Derek Zhou <derekzhou@gmail.com>
|
|
|
|
Written by: Richard Frith-Macdonald <rfm@gnu.org>
|
|
|
|
Date: 2006
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
License along with this library; if not, write to the Free
|
|
|
|
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
Boston, MA 02111 USA.
|
|
|
|
|
|
|
|
*/
|
2006-03-01 20:03:35 +00:00
|
|
|
#include "config.h"
|
|
|
|
#include "GNUstepBase/preface.h"
|
|
|
|
#include <winsock2.h>
|
|
|
|
#include <io.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
|
|
#include <unistd.h>
|
|
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2006-02-15 17:34:47 +00:00
|
|
|
|
|
|
|
#include <Foundation/NSData.h>
|
|
|
|
#include <Foundation/NSArray.h>
|
|
|
|
#include <Foundation/NSRunLoop.h>
|
|
|
|
#include <Foundation/NSException.h>
|
|
|
|
#include <Foundation/NSError.h>
|
|
|
|
#include <Foundation/NSValue.h>
|
|
|
|
#include <Foundation/NSHost.h>
|
2006-03-21 15:33:05 +00:00
|
|
|
#include <Foundation/NSProcessInfo.h>
|
2006-02-15 17:34:47 +00:00
|
|
|
|
|
|
|
#include "../GSStream.h"
|
|
|
|
|
2006-03-26 06:24:55 +00:00
|
|
|
#define BUFFERSIZE (BUFSIZ*64)
|
|
|
|
|
2006-03-01 20:03:35 +00:00
|
|
|
typedef int socklen_t;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The concrete subclass of NSInputStream that reads from a file
|
|
|
|
*/
|
|
|
|
@interface GSFileInputStream : GSInputStream
|
|
|
|
{
|
|
|
|
@private
|
|
|
|
NSString *_path;
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
@end
|
|
|
|
|
2006-08-10 20:39:33 +00:00
|
|
|
@class GSPipeOutputStream;
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
/**
|
|
|
|
* The concrete subclass of NSInputStream that reads from a pipe
|
2006-03-01 20:03:35 +00:00
|
|
|
*/
|
2006-03-21 15:33:05 +00:00
|
|
|
@interface GSPipeInputStream : GSInputStream
|
|
|
|
{
|
|
|
|
HANDLE handle;
|
|
|
|
OVERLAPPED ov;
|
2006-03-26 06:24:55 +00:00
|
|
|
uint8_t data[BUFFERSIZE];
|
2006-03-21 15:33:05 +00:00
|
|
|
unsigned offset; // Read pointer within buffer
|
|
|
|
unsigned length; // Amount of data in buffer
|
|
|
|
unsigned want; // Amount of data we want to read.
|
|
|
|
DWORD size; // Number of bytes returned by read.
|
2006-08-10 20:39:33 +00:00
|
|
|
GSPipeOutputStream *_sibling;
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
- (NSStreamStatus) _check;
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _queue;
|
|
|
|
- (void) _setHandle: (HANDLE)h;
|
2006-08-10 20:39:33 +00:00
|
|
|
- (void) setSibling: (GSPipeOutputStream*)s;
|
2006-03-01 20:03:35 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@class GSSocketOutputStream;
|
2006-08-10 20:39:33 +00:00
|
|
|
|
2006-03-01 20:03:35 +00:00
|
|
|
/**
|
|
|
|
* The abstract subclass of NSInputStream that reads from a socket
|
|
|
|
*/
|
2006-03-21 15:33:05 +00:00
|
|
|
@interface GSSocketInputStream : GSInputStream
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
|
|
|
@protected
|
|
|
|
GSSocketOutputStream *_sibling;
|
|
|
|
BOOL _passive; /* YES means already connected */
|
2006-03-21 16:22:42 +00:00
|
|
|
SOCKET _sock;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get the length of the socket addr
|
|
|
|
*/
|
|
|
|
- (socklen_t) sockLen;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get the sockaddr
|
|
|
|
*/
|
|
|
|
- (struct sockaddr*) peerAddr;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* setter for sibling
|
|
|
|
*/
|
2006-08-10 20:39:33 +00:00
|
|
|
- (void) setSibling: (GSSocketOutputStream*)sibling;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* setter for passive
|
|
|
|
*/
|
|
|
|
- (void) setPassive: (BOOL)passive;
|
|
|
|
|
|
|
|
- (void) setEvent: (WSAEVENT)event;
|
2006-03-21 16:22:42 +00:00
|
|
|
- (void) setSock: (SOCKET)sock;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface GSInetInputStream : GSSocketInputStream
|
|
|
|
{
|
|
|
|
@private
|
|
|
|
struct sockaddr_in _peerAddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* the designated initializer
|
|
|
|
*/
|
|
|
|
- (id) initToAddr: (NSString*)addr port: (int)port;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The concrete subclass of NSOutputStream that writes to a file
|
|
|
|
*/
|
|
|
|
@interface GSFileOutputStream : GSOutputStream
|
|
|
|
{
|
|
|
|
@private
|
|
|
|
NSString *_path;
|
|
|
|
BOOL _shouldAppend;
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The concrete subclass of NSOutputStream that reads from a pipe
|
2006-03-01 20:03:35 +00:00
|
|
|
*/
|
2006-03-21 15:33:05 +00:00
|
|
|
@interface GSPipeOutputStream : GSOutputStream
|
|
|
|
{
|
|
|
|
HANDLE handle;
|
|
|
|
OVERLAPPED ov;
|
2006-03-26 06:24:55 +00:00
|
|
|
uint8_t data[BUFFERSIZE];
|
2006-03-21 15:33:05 +00:00
|
|
|
unsigned offset;
|
|
|
|
unsigned want;
|
|
|
|
DWORD size;
|
2006-08-10 20:39:33 +00:00
|
|
|
GSPipeInputStream *_sibling;
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
- (NSStreamStatus) _check;
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _queue;
|
|
|
|
- (void) _setHandle: (HANDLE)h;
|
2006-08-10 20:39:33 +00:00
|
|
|
- (void) setSibling: (GSPipeInputStream*)s;
|
2006-03-01 20:03:35 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The concrete subclass of NSOutputStream that writes to a socket
|
|
|
|
*/
|
2006-03-21 15:33:05 +00:00
|
|
|
@interface GSSocketOutputStream : GSOutputStream
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
|
|
|
@protected
|
|
|
|
GSSocketInputStream *_sibling;
|
|
|
|
BOOL _passive; /* YES means already connected */
|
2006-03-21 16:22:42 +00:00
|
|
|
SOCKET _sock;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get the length of the socket addr
|
|
|
|
*/
|
|
|
|
- (socklen_t) sockLen;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* get the sockaddr
|
|
|
|
*/
|
|
|
|
- (struct sockaddr*) peerAddr;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* setter for sibling
|
|
|
|
*/
|
2006-08-10 20:39:33 +00:00
|
|
|
- (void) setSibling: (GSSocketInputStream*)sibling;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* setter for passive
|
|
|
|
*/
|
|
|
|
- (void) setPassive: (BOOL)passive;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* setter for event
|
|
|
|
*/
|
|
|
|
- (void) setEvent: (WSAEVENT)event;
|
2006-03-21 16:22:42 +00:00
|
|
|
- (void) setSock: (SOCKET)sock;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface GSInetOutputStream : GSSocketOutputStream
|
|
|
|
{
|
|
|
|
@private
|
|
|
|
struct sockaddr_in _peerAddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* the designated initializer
|
|
|
|
*/
|
|
|
|
- (id) initToAddr: (NSString*)addr port: (int)port;
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The concrete subclass of NSServerStream that accept connection from a socket
|
|
|
|
*/
|
2006-03-21 15:33:05 +00:00
|
|
|
@interface GSSocketServerStream : GSAbstractServerStream
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 16:22:42 +00:00
|
|
|
SOCKET _sock;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
|
2006-08-10 20:39:33 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The concrete subclass of NSServerStream that accepts named pipe connection
|
|
|
|
*/
|
|
|
|
@interface GSLocalServerStream : GSAbstractServerStream
|
|
|
|
{
|
|
|
|
NSString *path;
|
|
|
|
HANDLE handle;
|
|
|
|
OVERLAPPED ov;
|
|
|
|
}
|
|
|
|
@end
|
|
|
|
|
2006-03-01 20:03:35 +00:00
|
|
|
static void setNonblocking(SOCKET fd)
|
|
|
|
{
|
|
|
|
unsigned long dummy = 1;
|
|
|
|
|
|
|
|
if (ioctlsocket(fd, FIONBIO, &dummy) == SOCKET_ERROR)
|
|
|
|
NSLog(@"unable to set non-blocking mode - %s",GSLastErrorStr(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
@implementation GSFileInputStream
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) close
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-09 17:14:30 +00:00
|
|
|
if (_loopID != (void*)INVALID_HANDLE_VALUE)
|
2006-08-09 14:21:39 +00:00
|
|
|
{
|
|
|
|
if (CloseHandle((HANDLE)_loopID) == 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
[super close];
|
2006-08-09 14:21:39 +00:00
|
|
|
_loopID = (void*)INVALID_HANDLE_VALUE;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
if ([self _isOpened])
|
2006-08-10 20:39:33 +00:00
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
RELEASE(_path);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasBytesAvailable
|
|
|
|
{
|
|
|
|
if ([self _isOpened] && [self streamStatus] != NSStreamStatusAtEnd)
|
|
|
|
return YES;
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (id) initWithFileAtPath: (NSString *)path
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
if ((self = [super init]) != nil)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
ASSIGN(_path, path);
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
return self;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) open
|
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
HANDLE h;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
h = (void*)CreateFileW([_path fileSystemRepresentation],
|
|
|
|
GENERIC_READ,
|
|
|
|
FILE_SHARE_READ,
|
|
|
|
0,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
0,
|
|
|
|
0);
|
|
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
[self _recordError];
|
|
|
|
return;
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
[self _setLoopID: (void*)h];
|
2006-03-01 20:03:35 +00:00
|
|
|
[super open];
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) propertyForKey: (NSString *)key
|
|
|
|
{
|
|
|
|
if ([key isEqualToString: NSStreamFileCurrentOffsetKey])
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
DWORD offset = 0;
|
|
|
|
|
|
|
|
if ([self _isOpened])
|
|
|
|
offset = SetFilePointer((HANDLE)_loopID, 0, 0, FILE_CURRENT);
|
|
|
|
return [NSNumber numberWithLong: (long)offset];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
return [super propertyForKey: key];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
DWORD readLen;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
_events &= ~NSStreamEventHasBytesAvailable;
|
2006-03-21 15:33:05 +00:00
|
|
|
if (ReadFile((HANDLE)_loopID, buffer, len, &readLen, NULL) == 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else if (readLen == 0)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
[self _setStatus: NSStreamStatusAtEnd];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
return (int)readLen;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
|
2006-03-01 20:03:35 +00:00
|
|
|
- (void) _dispatch
|
|
|
|
{
|
|
|
|
BOOL av = [self hasBytesAvailable];
|
|
|
|
NSStreamEvent myEvent = av ? NSStreamEventHasBytesAvailable :
|
|
|
|
NSStreamEventEndEncountered;
|
2006-08-08 13:31:50 +00:00
|
|
|
NSStreamStatus myStatus = av ? NSStreamStatusOpen :
|
2006-03-01 20:03:35 +00:00
|
|
|
NSStreamStatusAtEnd;
|
|
|
|
|
|
|
|
[self _setStatus: myStatus];
|
|
|
|
[self _sendEvent: myEvent];
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSPipeInputStream
|
|
|
|
|
|
|
|
- (void) close
|
|
|
|
{
|
|
|
|
if (want > 0 && handle != INVALID_HANDLE_VALUE)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
want = 0;
|
|
|
|
CancelIo(handle);
|
|
|
|
}
|
|
|
|
if (_loopID != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
CloseHandle((HANDLE)_loopID);
|
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
if (handle != INVALID_HANDLE_VALUE && [_sibling _isOpened] == NO)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
|
|
|
if (CloseHandle(handle) == 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
length = offset = 0;
|
|
|
|
[super close];
|
|
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
_loopID = (void*)INVALID_HANDLE_VALUE;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) dealloc
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
|
|
|
if ([self _isOpened])
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
|
|
|
[self close];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
[_sibling setSibling: nil];
|
|
|
|
_sibling = nil;
|
2006-03-21 15:33:05 +00:00
|
|
|
[super dealloc];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
if (offset < length)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
*buffer = data + offset;
|
|
|
|
*len = length - offset;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasBytesAvailable
|
|
|
|
{
|
2006-08-10 06:23:08 +00:00
|
|
|
if ([self streamStatus] == NSStreamStatusOpen)
|
2006-03-21 15:33:05 +00:00
|
|
|
return YES;
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
{
|
|
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
_loopID = (void*)INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) open
|
|
|
|
{
|
|
|
|
if (_loopID == (void*)INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
_loopID = (void*)CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
}
|
|
|
|
[super open];
|
|
|
|
[self _queue];
|
|
|
|
}
|
|
|
|
|
2006-03-26 06:24:55 +00:00
|
|
|
- (NSStreamStatus) _check
|
|
|
|
{
|
|
|
|
// Must only be called when current status is NSStreamStatusReading.
|
|
|
|
|
|
|
|
if (GetOverlappedResult(handle, &ov, &size, TRUE) == 0)
|
|
|
|
{
|
|
|
|
errno = GetLastError();
|
|
|
|
if (errno == ERROR_HANDLE_EOF
|
|
|
|
|| errno == ERROR_BROKEN_PIPE)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Got EOF, but we don't want to register it until a
|
|
|
|
* -read:maxLength: is called.
|
|
|
|
*/
|
|
|
|
offset = length = want = 0;
|
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
|
|
|
}
|
|
|
|
else if (errno != ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Got an error ... record it.
|
|
|
|
*/
|
|
|
|
want = 0;
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Read completed and some data was read.
|
|
|
|
*/
|
|
|
|
length = size;
|
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
|
|
|
}
|
|
|
|
return [self streamStatus];
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _queue
|
|
|
|
{
|
|
|
|
if ([self streamStatus] == NSStreamStatusOpen)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
want = sizeof(data);
|
|
|
|
ov.Offset = 0;
|
|
|
|
ov.OffsetHigh = 0;
|
|
|
|
ov.hEvent = (HANDLE)_loopID;
|
|
|
|
rc = ReadFile(handle, data, want, &size, &ov);
|
|
|
|
if (rc != 0)
|
|
|
|
{
|
|
|
|
// Read succeeded
|
|
|
|
want = 0;
|
|
|
|
length = size;
|
|
|
|
if (length == 0)
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusAtEnd];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if ((errno = GetLastError()) == ERROR_HANDLE_EOF
|
|
|
|
|| errno == ERROR_BROKEN_PIPE)
|
|
|
|
{
|
|
|
|
offset = length = 0;
|
|
|
|
[self _setStatus: NSStreamStatusOpen]; // Read of zero length
|
|
|
|
}
|
|
|
|
else if (errno != ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusReading];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
|
|
|
|
{
|
|
|
|
NSStreamStatus myStatus = [self streamStatus];
|
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
_events &= ~NSStreamEventHasBytesAvailable;
|
2006-03-26 06:24:55 +00:00
|
|
|
if (myStatus == NSStreamStatusReading)
|
|
|
|
{
|
|
|
|
myStatus = [self _check];
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
if (myStatus == NSStreamStatusAtEnd)
|
|
|
|
{
|
|
|
|
return 0; // At EOF
|
|
|
|
}
|
|
|
|
if (len <= 0
|
|
|
|
|| (myStatus != NSStreamStatusReading && myStatus != NSStreamStatusOpen))
|
|
|
|
{
|
|
|
|
return -1; // Bad length or status
|
|
|
|
}
|
|
|
|
if (offset == length)
|
|
|
|
{
|
|
|
|
if (myStatus == NSStreamStatusOpen)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* There is no buffered data and no read in progress,
|
|
|
|
* so we must be at EOF.
|
|
|
|
*/
|
|
|
|
[self _setStatus: NSStreamStatusAtEnd];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -1; // Waiting for read.
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We already have data buffered ... return some or all of it.
|
|
|
|
*/
|
|
|
|
if (len > (length - offset))
|
|
|
|
{
|
|
|
|
len = length - offset;
|
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
memcpy(buffer, data + offset, len);
|
2006-03-21 15:33:05 +00:00
|
|
|
offset += len;
|
2006-08-10 20:39:33 +00:00
|
|
|
if (offset == length)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
|
|
|
length = 0;
|
|
|
|
offset = 0;
|
2006-08-10 20:39:33 +00:00
|
|
|
if (myStatus == NSStreamStatusOpen)
|
|
|
|
{
|
|
|
|
[self _queue]; // Queue another read
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _setHandle: (HANDLE)h
|
|
|
|
{
|
|
|
|
handle = h;
|
|
|
|
}
|
|
|
|
|
2006-08-10 20:39:33 +00:00
|
|
|
- (void) setSibling: (GSPipeOutputStream*)s
|
|
|
|
{
|
|
|
|
_sibling = s;
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _dispatch
|
|
|
|
{
|
|
|
|
NSStreamEvent myEvent;
|
2006-03-26 06:24:55 +00:00
|
|
|
NSStreamStatus oldStatus = [self streamStatus];
|
|
|
|
NSStreamStatus myStatus = oldStatus;
|
2006-03-21 15:33:05 +00:00
|
|
|
|
2006-03-26 06:24:55 +00:00
|
|
|
if (myStatus == NSStreamStatusReading
|
|
|
|
|| myStatus == NSStreamStatusOpening)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
2006-03-26 06:24:55 +00:00
|
|
|
myStatus = [self _check];
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (myStatus == NSStreamStatusAtEnd)
|
|
|
|
{
|
|
|
|
myEvent = NSStreamEventEndEncountered;
|
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
else if (myStatus == NSStreamStatusError)
|
|
|
|
{
|
|
|
|
myEvent = NSStreamEventErrorOccurred;
|
|
|
|
}
|
|
|
|
else if (oldStatus == NSStreamStatusOpening)
|
|
|
|
{
|
|
|
|
myEvent = NSStreamEventOpenCompleted;
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
myEvent = NSStreamEventHasBytesAvailable;
|
|
|
|
}
|
|
|
|
|
|
|
|
[self _sendEvent: myEvent];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) runLoopShouldBlock: (BOOL*)trigger
|
|
|
|
{
|
|
|
|
NSStreamStatus myStatus = [self streamStatus];
|
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
if ([self _unhandledData] == YES || myStatus == NSStreamStatusError)
|
2006-08-08 13:31:50 +00:00
|
|
|
{
|
|
|
|
*trigger = NO;
|
|
|
|
return NO;
|
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
*trigger = YES;
|
2006-03-21 15:33:05 +00:00
|
|
|
if (myStatus == NSStreamStatusReading)
|
|
|
|
{
|
2006-03-26 06:24:55 +00:00
|
|
|
return YES; // Need to wait for I/O
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
return NO; // Need to signal for an event
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSSocketInputStream
|
|
|
|
|
|
|
|
- (socklen_t) sockLen
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (struct sockaddr*) peerAddr
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-08-10 20:39:33 +00:00
|
|
|
- (void) setSibling: (GSSocketOutputStream*)sibling
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
_sibling = sibling;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-(void) setPassive: (BOOL)passive
|
|
|
|
{
|
|
|
|
_passive = passive;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setEvent: (WSAEVENT)event
|
|
|
|
{
|
2006-03-21 16:22:42 +00:00
|
|
|
_loopID = event;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setSock: (SOCKET)sock
|
|
|
|
{
|
|
|
|
_sock = sock;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
{
|
|
|
|
_sibling = nil;
|
|
|
|
_passive = NO;
|
2006-03-21 16:22:42 +00:00
|
|
|
_loopID = WSA_INVALID_EVENT;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
if ([self _isOpened])
|
2006-08-10 20:39:33 +00:00
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
|
|
|
[_sibling setSibling: nil];
|
|
|
|
_sibling = nil;
|
2006-03-01 20:03:35 +00:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) open
|
|
|
|
{
|
|
|
|
// could be opened because of sibling
|
|
|
|
if ([self _isOpened])
|
|
|
|
return;
|
|
|
|
if (_passive || (_sibling && [_sibling _isOpened]))
|
|
|
|
goto open_ok;
|
|
|
|
// check sibling status, avoid double connect
|
|
|
|
if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening)
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusOpening];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-03-21 16:22:42 +00:00
|
|
|
int connectReturn = connect(_sock, [self peerAddr], [self sockLen]);
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
if (connectReturn == SOCKET_ERROR
|
|
|
|
&& WSAGetLastError() != WSAEWOULDBLOCK)
|
|
|
|
{// make an error
|
|
|
|
[self _recordError];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// waiting on writable, as an indication of opened
|
|
|
|
if (_runloop)
|
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
unsigned i = [_modes count];
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-03-21 16:22:42 +00:00
|
|
|
WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS);
|
2006-03-21 15:33:05 +00:00
|
|
|
while (i-- > 0)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 16:22:42 +00:00
|
|
|
[_runloop addStream: self mode: [_modes objectAtIndex: i]];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
[self _setStatus: NSStreamStatusOpening];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
open_ok:
|
|
|
|
[super open];
|
2006-03-21 16:22:42 +00:00
|
|
|
setNonblocking(_sock);
|
|
|
|
WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS);
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) close
|
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
if (_loopID != WSA_INVALID_EVENT)
|
2006-08-09 17:14:30 +00:00
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
WSACloseEvent(_loopID);
|
2006-08-09 17:14:30 +00:00
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
if (_sock != INVALID_SOCKET)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
if (_sibling && [_sibling streamStatus] != NSStreamStatusClosed)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Windows only permits a single event to be associated with a socket
|
|
|
|
* at any time, but the runloop system only allows an event handle to
|
|
|
|
* be added to the loop once, and we have two streams for each socket.
|
|
|
|
* So we use two events, one for each stream, and when one stream is
|
|
|
|
* closed, we must call WSAEventSelect to ensure that the event handle
|
|
|
|
* of the sibling is used to signal events from now on.
|
|
|
|
*/
|
|
|
|
WSAEventSelect(_sock, [_sibling _loopID], FD_ALL_EVENTS);
|
|
|
|
shutdown(_sock, SD_RECEIVE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
closesocket(_sock);
|
|
|
|
}
|
|
|
|
_sock = INVALID_SOCKET;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
[super close];
|
2006-08-09 17:14:30 +00:00
|
|
|
_loopID = WSA_INVALID_EVENT;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
|
|
|
|
{
|
|
|
|
int readLen;
|
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
_events &= ~NSStreamEventHasBytesAvailable;
|
2006-03-21 16:22:42 +00:00
|
|
|
readLen = recv(_sock, buffer, len, 0);
|
2006-03-01 20:03:35 +00:00
|
|
|
if (readLen == SOCKET_ERROR)
|
|
|
|
{
|
|
|
|
errno = WSAGetLastError();
|
|
|
|
if (errno == WSAEINPROGRESS || errno == WSAEWOULDBLOCK)
|
2006-08-08 13:31:50 +00:00
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusReading];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
else if (errno != WSAEINTR)
|
2006-08-08 13:31:50 +00:00
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
2006-08-09 17:14:30 +00:00
|
|
|
readLen = -1;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
else if (readLen == 0)
|
2006-08-08 13:31:50 +00:00
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusAtEnd];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
else
|
2006-08-08 13:31:50 +00:00
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
return readLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasBytesAvailable
|
|
|
|
{
|
2006-08-10 06:23:08 +00:00
|
|
|
if ([self streamStatus] == NSStreamStatusOpen)
|
2006-03-01 20:03:35 +00:00
|
|
|
return YES;
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _dispatch
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-10 05:50:08 +00:00
|
|
|
/*
|
|
|
|
* Windows only permits a single event to be associated with a socket
|
|
|
|
* at any time, but the runloop system only allows an event handle to
|
|
|
|
* be added to the loop once, and we have two streams for each socket.
|
|
|
|
* So we use two events, one for each stream, and the _dispatch method
|
|
|
|
* must handle things for both streams.
|
|
|
|
*/
|
2006-03-01 20:03:35 +00:00
|
|
|
if ([self streamStatus] == NSStreamStatusClosed)
|
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
/*
|
|
|
|
* It is possible the stream is closed yet recieving event because
|
|
|
|
* of not closed sibling
|
|
|
|
*/
|
2006-03-01 20:03:35 +00:00
|
|
|
NSAssert([_sibling streamStatus] != NSStreamStatusClosed,
|
2006-08-08 13:31:50 +00:00
|
|
|
@"Received event for closed stream");
|
2006-03-21 15:33:05 +00:00
|
|
|
[_sibling _dispatch];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
else
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
WSANETWORKEVENTS events;
|
|
|
|
int error = 0;
|
|
|
|
int getReturn = -1;
|
2006-03-21 15:33:05 +00:00
|
|
|
|
2006-08-08 13:31:50 +00:00
|
|
|
if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
error = WSAGetLastError();
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
//else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
|
2006-08-08 13:31:50 +00:00
|
|
|
|
|
|
|
if ([self streamStatus] == NSStreamStatusOpening)
|
|
|
|
{
|
|
|
|
unsigned i = [_modes count];
|
|
|
|
|
|
|
|
while (i-- > 0)
|
2006-08-07 20:01:00 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
[_runloop removeStream: self mode: [_modes objectAtIndex: i]];
|
2006-08-07 20:01:00 +00:00
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
if (error == 0)
|
|
|
|
{
|
|
|
|
unsigned len = sizeof(error);
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-08-08 13:31:50 +00:00
|
|
|
getReturn = getsockopt(_sock, SOL_SOCKET, SO_ERROR,
|
|
|
|
(char*)&error, &len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getReturn >= 0 && error == 0
|
|
|
|
&& (events.lNetworkEvents & FD_CONNECT))
|
|
|
|
{ // finish up the opening
|
|
|
|
_passive = YES;
|
|
|
|
[self open];
|
|
|
|
// notify sibling
|
|
|
|
if (_sibling)
|
|
|
|
{
|
|
|
|
[_sibling open];
|
|
|
|
[_sibling _sendEvent: NSStreamEventOpenCompleted];
|
|
|
|
}
|
|
|
|
[self _sendEvent: NSStreamEventOpenCompleted];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error != 0)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
errno = error;
|
|
|
|
[self _recordError];
|
|
|
|
[_sibling _recordError];
|
2006-03-01 20:03:35 +00:00
|
|
|
[self _sendEvent: NSStreamEventErrorOccurred];
|
2006-08-08 13:31:50 +00:00
|
|
|
[_sibling _sendEvent: NSStreamEventErrorOccurred];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
else
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
if (events.lNetworkEvents & FD_WRITE)
|
|
|
|
{
|
2006-08-10 09:15:30 +00:00
|
|
|
NSAssert([_sibling _isOpened], NSInternalInconsistencyException);
|
|
|
|
/* Clear NSStreamStatusWriting if it was set */
|
2006-08-08 13:31:50 +00:00
|
|
|
[_sibling _setStatus: NSStreamStatusOpen];
|
|
|
|
}
|
2006-08-10 09:15:30 +00:00
|
|
|
/* On winsock a socket is always writable unless it has had
|
|
|
|
* failure/closure or a write blocked and we have not been
|
|
|
|
* signalled again.
|
|
|
|
*/
|
|
|
|
while ([_sibling _unhandledData] == NO
|
|
|
|
&& [_sibling hasSpaceAvailable])
|
|
|
|
{
|
|
|
|
[_sibling _sendEvent: NSStreamEventHasSpaceAvailable];
|
|
|
|
}
|
|
|
|
|
2006-08-08 13:31:50 +00:00
|
|
|
if (events.lNetworkEvents & FD_READ)
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
2006-08-09 17:14:30 +00:00
|
|
|
while ([self hasBytesAvailable]
|
2006-08-10 09:15:30 +00:00
|
|
|
&& [self _unhandledData] == NO)
|
2006-08-09 17:14:30 +00:00
|
|
|
{
|
|
|
|
[self _sendEvent: NSStreamEventHasBytesAvailable];
|
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
}
|
|
|
|
if (events.lNetworkEvents & FD_CLOSE)
|
|
|
|
{
|
2006-08-10 05:50:08 +00:00
|
|
|
if ([_sibling _isOpened])
|
|
|
|
{
|
|
|
|
[_sibling _setStatus: NSStreamStatusAtEnd];
|
|
|
|
[_sibling _sendEvent: NSStreamEventEndEncountered];
|
|
|
|
}
|
2006-08-09 17:14:30 +00:00
|
|
|
while ([self hasBytesAvailable]
|
2006-08-10 09:15:30 +00:00
|
|
|
&& [self _unhandledData] == NO)
|
2006-08-08 13:31:50 +00:00
|
|
|
{
|
|
|
|
[self _sendEvent: NSStreamEventHasBytesAvailable];
|
|
|
|
}
|
2006-08-10 05:50:08 +00:00
|
|
|
if ([self _isOpened])
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusAtEnd];
|
|
|
|
[self _sendEvent: NSStreamEventEndEncountered];
|
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-09 21:20:50 +00:00
|
|
|
- (BOOL) runLoopShouldBlock: (BOOL*)trigger
|
|
|
|
{
|
|
|
|
*trigger = YES;
|
|
|
|
return YES;
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSInetInputStream
|
|
|
|
|
|
|
|
- (socklen_t) sockLen
|
|
|
|
{
|
|
|
|
return sizeof(struct sockaddr_in);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (struct sockaddr*) peerAddr
|
|
|
|
{
|
|
|
|
return (struct sockaddr*)&_peerAddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initToAddr: (NSString*)addr port: (int)port
|
|
|
|
{
|
|
|
|
const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
{
|
|
|
|
_peerAddr.sin_family = AF_INET;
|
|
|
|
_peerAddr.sin_port = htons(port);
|
|
|
|
_peerAddr.sin_addr.s_addr = inet_addr(addr_c);
|
|
|
|
if (_peerAddr.sin_addr.s_addr == INADDR_NONE) // error
|
|
|
|
{
|
|
|
|
DESTROY(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSFileOutputStream
|
|
|
|
|
2006-08-09 14:21:39 +00:00
|
|
|
- (void) close
|
|
|
|
{
|
2006-08-09 17:14:30 +00:00
|
|
|
if (_loopID != (void*)INVALID_HANDLE_VALUE)
|
2006-08-09 14:21:39 +00:00
|
|
|
{
|
|
|
|
if (CloseHandle((HANDLE)_loopID) == 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[super close];
|
|
|
|
_loopID = (void*)INVALID_HANDLE_VALUE;
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
if ([self _isOpened])
|
2006-08-10 20:39:33 +00:00
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
RELEASE(_path);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasSpaceAvailable
|
|
|
|
{
|
2006-08-10 06:23:08 +00:00
|
|
|
if ([self streamStatus] == NSStreamStatusOpen)
|
2006-03-01 20:03:35 +00:00
|
|
|
return YES;
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (id) initToFileAtPath: (NSString *)path append: (BOOL)shouldAppend
|
|
|
|
{
|
|
|
|
if ((self = [super init]) != nil)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
ASSIGN(_path, path);
|
|
|
|
_shouldAppend = shouldAppend;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
return self;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) open
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
HANDLE h;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
h = (void*)CreateFileW([_path fileSystemRepresentation],
|
|
|
|
GENERIC_WRITE,
|
|
|
|
FILE_SHARE_WRITE,
|
|
|
|
0,
|
|
|
|
OPEN_ALWAYS,
|
|
|
|
0,
|
|
|
|
0);
|
|
|
|
if (h == INVALID_HANDLE_VALUE)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
[self _recordError];
|
|
|
|
return;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
else if (_shouldAppend == NO)
|
|
|
|
{
|
|
|
|
if (SetEndOfFile(h) == 0) // Truncate to current file pointer (0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
[self _setLoopID: (void*)h];
|
|
|
|
[super open];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) propertyForKey: (NSString *)key
|
|
|
|
{
|
|
|
|
if ([key isEqualToString: NSStreamFileCurrentOffsetKey])
|
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
DWORD offset = 0;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
if ([self _isOpened])
|
2006-03-21 15:33:05 +00:00
|
|
|
offset = SetFilePointer((HANDLE)_loopID, 0, 0, FILE_CURRENT);
|
|
|
|
return [NSNumber numberWithLong: (long)offset];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
return [super propertyForKey: key];
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
|
|
|
|
{
|
|
|
|
DWORD writeLen;
|
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
_events &= ~NSStreamEventHasSpaceAvailable;
|
2006-03-21 15:33:05 +00:00
|
|
|
if (_shouldAppend == YES)
|
|
|
|
{
|
|
|
|
SetFilePointer((HANDLE)_loopID, 0, 0, FILE_END);
|
|
|
|
}
|
|
|
|
if (WriteFile((HANDLE)_loopID, buffer, len, &writeLen, NULL) == 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return (int)writeLen;
|
|
|
|
}
|
|
|
|
|
2006-03-04 06:47:56 +00:00
|
|
|
- (void) _dispatch
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
|
|
|
BOOL av = [self hasSpaceAvailable];
|
|
|
|
NSStreamEvent myEvent = av ? NSStreamEventHasSpaceAvailable :
|
|
|
|
NSStreamEventEndEncountered;
|
|
|
|
|
|
|
|
[self _sendEvent: myEvent];
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSPipeOutputStream
|
|
|
|
|
|
|
|
- (void) close
|
|
|
|
{
|
|
|
|
if (_loopID != INVALID_HANDLE_VALUE)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
CloseHandle((HANDLE)_loopID);
|
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
if (handle != INVALID_HANDLE_VALUE && [_sibling _isOpened] == NO)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
|
|
|
if (CloseHandle(handle) == 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
offset = want = 0;
|
|
|
|
[super close];
|
|
|
|
_loopID = (void*)INVALID_HANDLE_VALUE;
|
|
|
|
handle = INVALID_HANDLE_VALUE;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) dealloc
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
|
|
|
if ([self _isOpened])
|
2006-08-10 20:39:33 +00:00
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
|
|
|
[_sibling setSibling: nil];
|
|
|
|
_sibling = nil;
|
2006-03-21 15:33:05 +00:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasSpaceAvailable
|
|
|
|
{
|
2006-08-10 06:23:08 +00:00
|
|
|
if ([self streamStatus] == NSStreamStatusOpen)
|
2006-03-21 15:33:05 +00:00
|
|
|
return YES;
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]) != nil)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
_loopID = (void*)INVALID_HANDLE_VALUE;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
return self;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) open
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
if (_loopID == (void*)INVALID_HANDLE_VALUE)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
_loopID = (void*)CreateEvent(NULL, FALSE, FALSE, NULL);
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
[super open];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _queue
|
|
|
|
{
|
|
|
|
NSStreamStatus myStatus = [self streamStatus];
|
|
|
|
|
|
|
|
if (myStatus == NSStreamStatusOpen)
|
|
|
|
{
|
|
|
|
while (offset < want)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
ov.Offset = 0;
|
|
|
|
ov.OffsetHigh = 0;
|
|
|
|
ov.hEvent = (HANDLE)_loopID;
|
|
|
|
size = 0;
|
2006-03-26 06:24:55 +00:00
|
|
|
rc = WriteFile(handle, data + offset, want - offset, &size, &ov);
|
2006-03-21 15:33:05 +00:00
|
|
|
if (rc != 0)
|
|
|
|
{
|
|
|
|
offset += size;
|
2006-08-10 20:39:33 +00:00
|
|
|
if (offset == want)
|
|
|
|
{
|
|
|
|
offset = want = 0;
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
|
|
|
else if ((errno = GetLastError()) == ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusWriting];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
|
|
|
|
{
|
2006-03-26 06:24:55 +00:00
|
|
|
NSStreamStatus myStatus = [self streamStatus];
|
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
_events &= ~NSStreamEventHasSpaceAvailable;
|
2006-03-26 06:24:55 +00:00
|
|
|
if (len < 0)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
|
|
|
return -1;
|
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
if (myStatus == NSStreamStatusWriting)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
2006-03-26 06:24:55 +00:00
|
|
|
myStatus = [self _check];
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
if ((myStatus != NSStreamStatusOpen && myStatus != NSStreamStatusWriting))
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
2006-03-26 06:24:55 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (len > (sizeof(data) - offset))
|
|
|
|
{
|
|
|
|
len = sizeof(data) - offset;
|
|
|
|
}
|
|
|
|
if (len > 0)
|
|
|
|
{
|
|
|
|
memcpy(data + offset, buffer, len);
|
|
|
|
want = offset + len;
|
|
|
|
[self _queue];
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2006-03-26 06:24:55 +00:00
|
|
|
- (NSStreamStatus) _check
|
|
|
|
{
|
|
|
|
// Must only be called when current status is NSStreamStatusWriting.
|
|
|
|
if (GetOverlappedResult(handle, &ov, &size, TRUE) == 0)
|
|
|
|
{
|
|
|
|
errno = GetLastError();
|
|
|
|
if (errno != ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
offset = 0;
|
|
|
|
want = 0;
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
|
|
|
offset += size;
|
2006-08-10 20:39:33 +00:00
|
|
|
if (offset < want)
|
2006-03-26 06:24:55 +00:00
|
|
|
{
|
|
|
|
[self _queue];
|
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
offset = want = 0;
|
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
}
|
|
|
|
return [self streamStatus];
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _setHandle: (HANDLE)h
|
|
|
|
{
|
|
|
|
handle = h;
|
|
|
|
}
|
|
|
|
|
2006-08-10 20:39:33 +00:00
|
|
|
- (void) setSibling: (GSPipeInputStream*)s
|
|
|
|
{
|
|
|
|
_sibling = s;
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _dispatch
|
|
|
|
{
|
|
|
|
NSStreamEvent myEvent;
|
2006-03-26 06:24:55 +00:00
|
|
|
NSStreamStatus oldStatus = [self streamStatus];
|
|
|
|
NSStreamStatus myStatus = oldStatus;
|
2006-03-21 15:33:05 +00:00
|
|
|
|
2006-03-26 06:24:55 +00:00
|
|
|
if (myStatus == NSStreamStatusWriting
|
|
|
|
|| myStatus == NSStreamStatusOpening)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
2006-03-26 06:24:55 +00:00
|
|
|
myStatus = [self _check];
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
|
|
|
|
2006-03-26 06:24:55 +00:00
|
|
|
if (myStatus == NSStreamStatusAtEnd)
|
|
|
|
{
|
|
|
|
myEvent = NSStreamEventEndEncountered;
|
|
|
|
}
|
|
|
|
else if (myStatus == NSStreamStatusError)
|
|
|
|
{
|
|
|
|
myEvent = NSStreamEventErrorOccurred;
|
|
|
|
}
|
|
|
|
else if (oldStatus == NSStreamStatusOpening)
|
|
|
|
{
|
|
|
|
myEvent = NSStreamEventOpenCompleted;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
myEvent = NSStreamEventHasSpaceAvailable;
|
|
|
|
}
|
2006-03-21 15:33:05 +00:00
|
|
|
|
|
|
|
[self _sendEvent: myEvent];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) runLoopShouldBlock: (BOOL*)trigger
|
|
|
|
{
|
|
|
|
NSStreamStatus myStatus = [self streamStatus];
|
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
if ([self _unhandledData] == YES || myStatus == NSStreamStatusError)
|
2006-08-08 13:31:50 +00:00
|
|
|
{
|
|
|
|
*trigger = NO;
|
|
|
|
return NO;
|
|
|
|
}
|
2006-03-26 06:24:55 +00:00
|
|
|
*trigger = YES;
|
2006-03-21 15:33:05 +00:00
|
|
|
if (myStatus == NSStreamStatusWriting)
|
|
|
|
{
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSSocketOutputStream
|
|
|
|
|
|
|
|
- (socklen_t) sockLen
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (struct sockaddr*) peerAddr
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2006-08-10 20:39:33 +00:00
|
|
|
- (void) setSibling: (GSSocketInputStream*)sibling
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
_sibling = sibling;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
-(void)setPassive: (BOOL)passive
|
|
|
|
{
|
|
|
|
_passive = passive;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setEvent: (WSAEVENT)event
|
|
|
|
{
|
2006-03-21 16:22:42 +00:00
|
|
|
_loopID = event;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setSock: (SOCKET)sock
|
|
|
|
{
|
|
|
|
_sock = sock;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
{
|
|
|
|
_sibling = nil;
|
|
|
|
_passive = NO;
|
2006-03-21 16:22:42 +00:00
|
|
|
_loopID = WSA_INVALID_EVENT;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
if ([self _isOpened])
|
2006-08-10 20:39:33 +00:00
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
|
|
|
[_sibling setSibling: nil];
|
|
|
|
_sibling = nil;
|
2006-03-01 20:03:35 +00:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
|
|
|
|
{
|
|
|
|
int writeLen;
|
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
_events &= ~NSStreamEventHasSpaceAvailable;
|
2006-03-21 16:22:42 +00:00
|
|
|
writeLen = send(_sock, buffer, len, 0);
|
2006-03-01 20:03:35 +00:00
|
|
|
if (writeLen == SOCKET_ERROR)
|
|
|
|
{
|
|
|
|
errno = WSAGetLastError();
|
|
|
|
if (errno == WSAEINPROGRESS || errno == WSAEWOULDBLOCK)
|
2006-08-09 17:14:30 +00:00
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusWriting];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
else if (errno != WSAEINTR)
|
2006-08-09 17:14:30 +00:00
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
writeLen = -1;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
else
|
2006-08-09 17:14:30 +00:00
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
return writeLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasSpaceAvailable
|
|
|
|
{
|
2006-08-10 06:23:08 +00:00
|
|
|
if ([self streamStatus] == NSStreamStatusOpen)
|
2006-03-01 20:03:35 +00:00
|
|
|
return YES;
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) open
|
|
|
|
{
|
|
|
|
// could be opened because of sibling
|
|
|
|
if ([self _isOpened])
|
|
|
|
return;
|
|
|
|
if (_passive || (_sibling && [_sibling _isOpened]))
|
|
|
|
goto open_ok;
|
|
|
|
// check sibling status, avoid double connect
|
|
|
|
if (_sibling && [_sibling streamStatus] == NSStreamStatusOpening)
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusOpening];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-03-21 16:22:42 +00:00
|
|
|
int connectReturn = connect(_sock, [self peerAddr], [self sockLen]);
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
if (connectReturn == SOCKET_ERROR
|
|
|
|
&& WSAGetLastError() != WSAEWOULDBLOCK)
|
|
|
|
{// make an error
|
|
|
|
[self _recordError];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// waiting on writable, as an indication of opened
|
|
|
|
if (_runloop)
|
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
unsigned i = [_modes count];
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-03-21 16:22:42 +00:00
|
|
|
WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS);
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
while (i-- > 0)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-03-21 16:22:42 +00:00
|
|
|
[_runloop addStream: self mode: [_modes objectAtIndex: i]];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
[self _setStatus: NSStreamStatusOpening];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
open_ok:
|
2006-03-21 16:22:42 +00:00
|
|
|
setNonblocking(_sock);
|
|
|
|
WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS);
|
2006-03-01 20:03:35 +00:00
|
|
|
[super open];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) close
|
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
if (_loopID != WSA_INVALID_EVENT)
|
2006-08-09 17:14:30 +00:00
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
WSACloseEvent(_loopID);
|
2006-08-09 17:14:30 +00:00
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
|
|
|
|
if (_sock != INVALID_SOCKET)
|
2006-08-09 17:14:30 +00:00
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
int closeReturn; // shutdown may fail (broken pipe). Record it.
|
|
|
|
|
|
|
|
if (_sibling && [_sibling streamStatus] != NSStreamStatusClosed)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Windows only permits a single event to be associated with a socket
|
|
|
|
* at any time, but the runloop system only allows an event handle to
|
|
|
|
* be added to the loop once, and we have two streams for each socket.
|
|
|
|
* So we use two events, one for each stream, and when one stream is
|
|
|
|
* closed, we must call WSAEventSelect to ensure that the event handle
|
|
|
|
* of the sibling is used to signal events from now on.
|
|
|
|
*/
|
|
|
|
WSAEventSelect(_sock, [_sibling _loopID], FD_ALL_EVENTS);
|
|
|
|
closeReturn = shutdown(_sock, SD_SEND);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
closeReturn = closesocket(_sock);
|
|
|
|
}
|
|
|
|
_sock = INVALID_SOCKET;
|
|
|
|
if (closeReturn < 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
2006-08-09 17:14:30 +00:00
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
[super close];
|
2006-08-09 17:14:30 +00:00
|
|
|
_loopID = WSA_INVALID_EVENT;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _dispatch
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-10 05:50:08 +00:00
|
|
|
/*
|
|
|
|
* Windows only permits a single event to be associated with a socket
|
|
|
|
* at any time, but the runloop system only allows an event handle to
|
|
|
|
* be added to the loop once, and we have two streams for each socket.
|
|
|
|
* So we use two events, one for each stream, and the _dispatch method
|
|
|
|
* must handle things for both streams.
|
|
|
|
*/
|
2006-03-01 20:03:35 +00:00
|
|
|
if ([self streamStatus] == NSStreamStatusClosed)
|
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
/*
|
|
|
|
* It is possible the stream is closed yet recieving event because
|
|
|
|
* of not closed sibling
|
|
|
|
*/
|
2006-03-01 20:03:35 +00:00
|
|
|
NSAssert([_sibling streamStatus] != NSStreamStatusClosed,
|
|
|
|
@"Received event for closed stream");
|
2006-03-21 15:33:05 +00:00
|
|
|
[_sibling _dispatch];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
else
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
WSANETWORKEVENTS events;
|
|
|
|
int error = 0;
|
|
|
|
int getReturn = -1;
|
2006-03-21 15:33:05 +00:00
|
|
|
|
2006-08-08 13:31:50 +00:00
|
|
|
if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR)
|
2006-03-21 15:33:05 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
error = WSAGetLastError();
|
2006-03-21 15:33:05 +00:00
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
//else NSLog(@"EVENTS 0x%x on %p", events.lNetworkEvents, self);
|
2006-08-08 13:31:50 +00:00
|
|
|
|
|
|
|
if ([self streamStatus] == NSStreamStatusOpening)
|
|
|
|
{
|
|
|
|
unsigned i = [_modes count];
|
|
|
|
|
|
|
|
while (i-- > 0)
|
2006-08-07 20:01:00 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
[_runloop removeStream: self mode: [_modes objectAtIndex: i]];
|
2006-08-07 20:01:00 +00:00
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
|
|
|
|
if (error == 0)
|
|
|
|
{
|
|
|
|
unsigned len = sizeof(error);
|
|
|
|
|
|
|
|
getReturn = getsockopt(_sock, SOL_SOCKET, SO_ERROR,
|
|
|
|
(char*)&error, &len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getReturn >= 0 && error == 0
|
|
|
|
&& (events.lNetworkEvents & FD_CONNECT))
|
|
|
|
{ // finish up the opening
|
|
|
|
events.lNetworkEvents ^= FD_CONNECT;
|
|
|
|
_passive = YES;
|
|
|
|
[self open];
|
|
|
|
// notify sibling
|
|
|
|
if (_sibling)
|
|
|
|
{
|
|
|
|
[_sibling open];
|
|
|
|
[_sibling _sendEvent: NSStreamEventOpenCompleted];
|
|
|
|
}
|
|
|
|
[self _sendEvent: NSStreamEventOpenCompleted];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error != 0)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
errno = error;
|
|
|
|
[self _recordError];
|
|
|
|
[_sibling _recordError];
|
2006-03-01 20:03:35 +00:00
|
|
|
[self _sendEvent: NSStreamEventErrorOccurred];
|
2006-08-08 13:31:50 +00:00
|
|
|
[_sibling _sendEvent: NSStreamEventErrorOccurred];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
else
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
if (events.lNetworkEvents & FD_WRITE)
|
|
|
|
{
|
2006-08-10 09:15:30 +00:00
|
|
|
/* Clear NSStreamStatusWriting if it was set */
|
2006-08-08 13:31:50 +00:00
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
|
|
|
}
|
2006-08-10 09:15:30 +00:00
|
|
|
|
|
|
|
/* On winsock a socket is always writable unless it has had
|
|
|
|
* failure/closure or a write blocked and we have not been
|
|
|
|
* signalled again.
|
|
|
|
*/
|
|
|
|
while ([self _unhandledData] == NO && [self hasSpaceAvailable])
|
|
|
|
{
|
|
|
|
[self _sendEvent: NSStreamEventHasSpaceAvailable];
|
|
|
|
}
|
|
|
|
|
2006-08-08 13:31:50 +00:00
|
|
|
if (events.lNetworkEvents & FD_READ)
|
|
|
|
{
|
|
|
|
[_sibling _setStatus: NSStreamStatusOpen];
|
2006-08-09 17:14:30 +00:00
|
|
|
while ([_sibling hasBytesAvailable]
|
|
|
|
&& [_sibling _unhandledData] == NO)
|
|
|
|
{
|
|
|
|
[_sibling _sendEvent: NSStreamEventHasBytesAvailable];
|
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
}
|
|
|
|
if (events.lNetworkEvents & FD_CLOSE)
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusAtEnd];
|
|
|
|
[self _sendEvent: NSStreamEventEndEncountered];
|
2006-08-09 17:14:30 +00:00
|
|
|
while ([_sibling hasBytesAvailable]
|
2006-08-08 13:31:50 +00:00
|
|
|
&& [_sibling _unhandledData] == NO)
|
|
|
|
{
|
|
|
|
[_sibling _sendEvent: NSStreamEventHasBytesAvailable];
|
|
|
|
}
|
2006-08-10 05:50:08 +00:00
|
|
|
if ([_sibling _isOpened])
|
|
|
|
{
|
|
|
|
[_sibling _setStatus: NSStreamStatusAtEnd];
|
|
|
|
[_sibling _sendEvent: NSStreamEventEndEncountered];
|
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-08-09 21:20:50 +00:00
|
|
|
- (BOOL) runLoopShouldBlock: (BOOL*)trigger
|
|
|
|
{
|
|
|
|
*trigger = YES;
|
2006-08-10 09:15:30 +00:00
|
|
|
if ([self _unhandledData] == NO && [self streamStatus] == NSStreamStatusOpen)
|
|
|
|
{
|
|
|
|
/* In winsock, a writable status is only signalled if an earlier
|
|
|
|
* write failed (because it would block), so we must simulate the
|
|
|
|
* writable event by having the run loop trigger without blocking.
|
|
|
|
*/
|
|
|
|
return NO;
|
|
|
|
}
|
2006-08-09 21:20:50 +00:00
|
|
|
return YES;
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation GSInetOutputStream
|
|
|
|
|
|
|
|
- (socklen_t) sockLen
|
|
|
|
{
|
|
|
|
return sizeof(struct sockaddr_in);
|
|
|
|
}
|
|
|
|
|
|
|
|
- (struct sockaddr*) peerAddr
|
|
|
|
{
|
|
|
|
return (struct sockaddr*)&_peerAddr;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initToAddr: (NSString*)addr port: (int)port
|
|
|
|
{
|
|
|
|
const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
{
|
|
|
|
_peerAddr.sin_family = AF_INET;
|
|
|
|
_peerAddr.sin_port = htons(port);
|
|
|
|
_peerAddr.sin_addr.s_addr = inet_addr(addr_c);
|
|
|
|
if (_peerAddr.sin_addr.s_addr == INADDR_NONE) // error
|
|
|
|
{
|
|
|
|
DESTROY(self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2006-02-15 17:34:47 +00:00
|
|
|
@implementation NSStream
|
|
|
|
|
|
|
|
+ (void) getStreamsToHost: (NSHost *)host
|
|
|
|
port: (int)port
|
|
|
|
inputStream: (NSInputStream **)inputStream
|
|
|
|
outputStream: (NSOutputStream **)outputStream
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
|
|
|
NSString *address = [host address];
|
|
|
|
GSSocketInputStream *ins = nil;
|
|
|
|
GSSocketOutputStream *outs = nil;
|
|
|
|
int sock;
|
2006-08-10 05:50:08 +00:00
|
|
|
WSAEVENT ievent;
|
|
|
|
WSAEVENT oevent;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
ins = AUTORELEASE([[GSInetInputStream alloc]
|
|
|
|
initToAddr: address port: port]);
|
|
|
|
outs = AUTORELEASE([[GSInetOutputStream alloc]
|
|
|
|
initToAddr: address port: port]);
|
|
|
|
sock = socket(PF_INET, SOCK_STREAM, 0);
|
2006-08-10 05:50:08 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Windows only permits a single event to be associated with a socket
|
|
|
|
* at any time, but the runloop system only allows an event handle to
|
|
|
|
* be added to the loop once, and we have two streams.
|
|
|
|
* So we create two events, one for each stream, so that we can have
|
|
|
|
* both streams scheduled in the run loop, but we make sure that the
|
|
|
|
* _dispatch method in each stream actually handles things for both
|
|
|
|
* streams so that whichever stream gets signalled, the correct
|
|
|
|
* actions are taken.
|
|
|
|
*/
|
|
|
|
ievent = CreateEvent(NULL, NO, NO, NULL);
|
|
|
|
oevent = CreateEvent(NULL, NO, NO, NULL);
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-08-10 20:39:33 +00:00
|
|
|
NSAssert(sock != INVALID_SOCKET, @"Cannot open socket");
|
2006-03-21 16:22:42 +00:00
|
|
|
[ins setSock: sock];
|
|
|
|
[outs setSock: sock];
|
2006-08-10 05:50:08 +00:00
|
|
|
[ins setEvent: ievent];
|
|
|
|
[outs setEvent: oevent];
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
if (inputStream)
|
|
|
|
{
|
|
|
|
[ins setSibling: outs];
|
|
|
|
*inputStream = ins;
|
|
|
|
}
|
|
|
|
if (outputStream)
|
|
|
|
{
|
|
|
|
[outs setSibling: ins];
|
|
|
|
*outputStream = outs;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
+ (void) getLocalStreamsToPath: (NSString *)path
|
|
|
|
inputStream: (NSInputStream **)inputStream
|
|
|
|
outputStream: (NSOutputStream **)outputStream
|
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
const unichar *name;
|
|
|
|
GSPipeInputStream *ins = nil;
|
|
|
|
GSPipeOutputStream *outs = nil;
|
|
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
|
|
HANDLE handle;
|
|
|
|
|
|
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
saAttr.bInheritHandle = TRUE;
|
|
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We allocate a new within the local pipe area
|
|
|
|
*/
|
|
|
|
name = [[@"\\\\.\\pipe\\" stringByAppendingString: path]
|
|
|
|
fileSystemRepresentation];
|
|
|
|
|
|
|
|
handle = CreateFileW(name,
|
|
|
|
GENERIC_WRITE|GENERIC_READ,
|
|
|
|
0,
|
|
|
|
&saAttr,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_FLAG_OVERLAPPED,
|
|
|
|
NULL);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Unable to open named pipe '%@'... %s",
|
|
|
|
path, GSLastErrorStr(GetLastError())];
|
|
|
|
}
|
|
|
|
|
|
|
|
// the type of the stream does not matter, since we are only using the fd
|
|
|
|
ins = AUTORELEASE([GSPipeInputStream new]);
|
|
|
|
outs = AUTORELEASE([GSPipeOutputStream new]);
|
|
|
|
|
|
|
|
[ins _setHandle: handle];
|
|
|
|
[outs _setHandle: handle];
|
|
|
|
if (inputStream)
|
|
|
|
{
|
|
|
|
[ins setSibling: outs];
|
|
|
|
*inputStream = ins;
|
|
|
|
}
|
|
|
|
if (outputStream)
|
|
|
|
{
|
|
|
|
[outs setSibling: ins];
|
|
|
|
*outputStream = outs;
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (void) pipeWithInputStream: (NSInputStream **)inputStream
|
|
|
|
outputStream: (NSOutputStream **)outputStream
|
2006-02-15 17:34:47 +00:00
|
|
|
{
|
2006-03-21 15:33:05 +00:00
|
|
|
const unichar *name;
|
|
|
|
GSPipeInputStream *ins = nil;
|
|
|
|
GSPipeOutputStream *outs = nil;
|
|
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
|
|
HANDLE readh;
|
|
|
|
HANDLE writeh;
|
|
|
|
HANDLE event;
|
|
|
|
OVERLAPPED ov;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
saAttr.bInheritHandle = TRUE;
|
|
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We have to use a named pipe since windows anonymous pipes do not
|
|
|
|
* support asynchronous I/O!
|
|
|
|
* We allocate a name known to be unique.
|
|
|
|
*/
|
|
|
|
name = [[@"\\\\.\\pipe\\" stringByAppendingString:
|
|
|
|
[[NSProcessInfo processInfo] globallyUniqueString]]
|
|
|
|
fileSystemRepresentation];
|
|
|
|
readh = CreateNamedPipeW(name,
|
|
|
|
PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
|
|
|
|
PIPE_TYPE_BYTE,
|
|
|
|
1,
|
|
|
|
BUFSIZ*64,
|
|
|
|
BUFSIZ*64,
|
|
|
|
100000,
|
|
|
|
&saAttr);
|
|
|
|
|
|
|
|
NSAssert(readh != INVALID_HANDLE_VALUE, @"Cannot create pipe");
|
|
|
|
|
|
|
|
// Start async connect
|
|
|
|
event = CreateEvent(NULL, NO, NO, NULL);
|
|
|
|
ov.Offset = 0;
|
|
|
|
ov.OffsetHigh = 0;
|
|
|
|
ov.hEvent = event;
|
|
|
|
ConnectNamedPipe(readh, &ov);
|
|
|
|
|
|
|
|
writeh = CreateFileW(name,
|
|
|
|
GENERIC_WRITE,
|
|
|
|
0,
|
|
|
|
&saAttr,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
FILE_FLAG_OVERLAPPED,
|
|
|
|
NULL);
|
|
|
|
if (writeh == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
CloseHandle(event);
|
|
|
|
CloseHandle(readh);
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Unable to create/open write pipe"];
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = WaitForSingleObject(event, 10);
|
|
|
|
CloseHandle(event);
|
|
|
|
|
|
|
|
if (rc != WAIT_OBJECT_0)
|
|
|
|
{
|
|
|
|
CloseHandle(readh);
|
|
|
|
CloseHandle(writeh);
|
|
|
|
[NSException raise: NSInternalInconsistencyException
|
|
|
|
format: @"Unable to create/open read pipe"];
|
|
|
|
}
|
|
|
|
|
|
|
|
// the type of the stream does not matter, since we are only using the fd
|
|
|
|
ins = AUTORELEASE([GSPipeInputStream new]);
|
|
|
|
outs = AUTORELEASE([GSPipeOutputStream new]);
|
|
|
|
|
|
|
|
[ins _setHandle: readh];
|
|
|
|
[outs _setHandle: writeh];
|
|
|
|
if (inputStream)
|
|
|
|
*inputStream = ins;
|
|
|
|
if (outputStream)
|
|
|
|
*outputStream = outs;
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) close
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) open
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) setDelegate: (id)delegate
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) delegate
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) setProperty: (id)property forKey: (NSString *)key
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) propertyForKey: (NSString *)key
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) scheduleInRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) removeFromRunLoop: (NSRunLoop *)aRunLoop forMode: (NSString *)mode;
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSError *) streamError
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (NSStreamStatus) streamStatus
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSInputStream
|
|
|
|
|
|
|
|
+ (id) inputStreamWithData: (NSData *)data
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
return AUTORELEASE([[GSMemoryInputStream alloc] initWithData: data]);
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) inputStreamWithFileAtPath: (NSString *)path
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
return AUTORELEASE([[GSFileInputStream alloc] initWithFileAtPath: path]);
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithData: (NSData *)data
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
RELEASE(self);
|
|
|
|
return [[GSMemoryInputStream alloc] initWithData: data];
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithFileAtPath: (NSString *)path
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
RELEASE(self);
|
|
|
|
return [[GSFileInputStream alloc] initWithFileAtPath: path];
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (int) read: (uint8_t *)buffer maxLength: (unsigned int)len
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) getBuffer: (uint8_t **)buffer length: (unsigned int *)len
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasBytesAvailable
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSOutputStream
|
|
|
|
|
|
|
|
+ (id) outputStreamToMemory
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
return AUTORELEASE([[GSMemoryOutputStream alloc]
|
|
|
|
initToBuffer: NULL capacity: 0]);
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) outputStreamToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
return AUTORELEASE([[GSMemoryOutputStream alloc]
|
|
|
|
initToBuffer: buffer capacity: capacity]);
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) outputStreamToFileAtPath: (NSString *)path append: (BOOL)shouldAppend
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
return AUTORELEASE([[GSFileOutputStream alloc]
|
|
|
|
initToFileAtPath: path append: shouldAppend]);
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initToMemory
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
RELEASE(self);
|
|
|
|
return [[GSMemoryOutputStream alloc] initToBuffer: NULL capacity: 0];
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initToBuffer: (uint8_t *)buffer capacity: (unsigned int)capacity
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
RELEASE(self);
|
|
|
|
return [[GSMemoryOutputStream alloc] initToBuffer: buffer capacity: capacity];
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initToFileAtPath: (NSString *)path append: (BOOL)shouldAppend
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
RELEASE(self);
|
|
|
|
return [[GSFileOutputStream alloc] initToFileAtPath: path
|
|
|
|
append: shouldAppend];
|
2006-02-15 17:34:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (int) write: (const uint8_t *)buffer maxLength: (unsigned int)len
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) hasSpaceAvailable
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2006-02-16 19:19:30 +00:00
|
|
|
@implementation GSServerStream
|
|
|
|
|
|
|
|
+ (id) serverStreamToAddr: (NSString*)addr port: (int)port
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
GSServerStream *s;
|
|
|
|
|
|
|
|
s = [[GSInetServerStream alloc] initToAddr: addr port: port];
|
|
|
|
return AUTORELEASE(s);
|
2006-02-16 19:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
+ (id) serverStreamToAddr: (NSString*)addr
|
|
|
|
{
|
2006-08-09 17:14:30 +00:00
|
|
|
GSServerStream *s;
|
|
|
|
|
2006-08-10 20:39:33 +00:00
|
|
|
s = [[GSLocalServerStream alloc] initToAddr: addr];
|
2006-08-09 17:14:30 +00:00
|
|
|
return AUTORELEASE(s);
|
2006-02-16 19:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initToAddr: (NSString*)addr port: (int)port
|
|
|
|
{
|
2006-03-01 20:03:35 +00:00
|
|
|
RELEASE(self);
|
|
|
|
self = [[GSInetServerStream alloc] initToAddr: addr port: port];
|
|
|
|
return self;
|
2006-02-16 19:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initToAddr: (NSString*)addr
|
|
|
|
{
|
2006-08-09 17:14:30 +00:00
|
|
|
RELEASE(self);
|
2006-08-10 20:39:33 +00:00
|
|
|
self = [[GSLocalServerStream alloc] initToAddr: addr];
|
|
|
|
return self;
|
2006-02-16 19:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) acceptWithInputStream: (NSInputStream **)inputStream
|
|
|
|
outputStream: (NSOutputStream **)outputStream
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2006-03-01 20:03:35 +00:00
|
|
|
@implementation GSSocketServerStream
|
|
|
|
|
|
|
|
- (Class) _inputStreamClass
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return Nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (Class) _outputStreamClass
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return Nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
if ((self = [super init]) != nil)
|
2006-08-10 20:39:33 +00:00
|
|
|
{
|
|
|
|
_loopID = WSA_INVALID_EVENT;
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
return self;
|
|
|
|
}
|
2006-08-10 20:39:33 +00:00
|
|
|
|
2006-03-01 20:03:35 +00:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
if ([self _isOpened])
|
2006-08-10 20:39:33 +00:00
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (socklen_t) sockLen
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (struct sockaddr*) serverAddr
|
|
|
|
{
|
|
|
|
[self subclassResponsibility: _cmd];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-08-08 13:31:50 +00:00
|
|
|
#define SOCKET_BACKLOG 255
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
- (void) open
|
|
|
|
{
|
2006-03-21 16:22:42 +00:00
|
|
|
int bindReturn = bind(_sock, [self serverAddr], [self sockLen]);
|
|
|
|
int listenReturn = listen(_sock, SOCKET_BACKLOG);
|
2006-03-01 20:03:35 +00:00
|
|
|
|
|
|
|
if (bindReturn < 0 || listenReturn)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
return;
|
|
|
|
}
|
2006-03-21 16:22:42 +00:00
|
|
|
setNonblocking(_sock);
|
|
|
|
_loopID = CreateEvent(NULL, NO, NO, NULL);
|
|
|
|
WSAEventSelect(_sock, _loopID, FD_ALL_EVENTS);
|
2006-03-21 15:33:05 +00:00
|
|
|
[super open];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) close
|
|
|
|
{
|
2006-08-10 20:39:33 +00:00
|
|
|
if (_loopID != WSA_INVALID_EVENT)
|
|
|
|
{
|
|
|
|
WSACloseEvent(_loopID);
|
|
|
|
}
|
|
|
|
if (_sock != INVALID_SOCKET)
|
|
|
|
{
|
|
|
|
// close a server socket is safe
|
|
|
|
closesocket(_sock);
|
|
|
|
_sock = INVALID_SOCKET;
|
|
|
|
}
|
2006-03-01 20:03:35 +00:00
|
|
|
[super close];
|
2006-08-09 17:14:30 +00:00
|
|
|
_loopID = WSA_INVALID_EVENT;
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) acceptWithInputStream: (NSInputStream **)inputStream
|
|
|
|
outputStream: (NSOutputStream **)outputStream
|
|
|
|
{
|
|
|
|
GSSocketInputStream *ins = AUTORELEASE([[self _inputStreamClass] new]);
|
|
|
|
GSSocketOutputStream *outs = AUTORELEASE([[self _outputStreamClass] new]);
|
|
|
|
socklen_t len = [ins sockLen];
|
2006-03-21 16:22:42 +00:00
|
|
|
int acceptReturn = accept(_sock, [ins peerAddr], &len);
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-08-10 09:15:30 +00:00
|
|
|
_events &= ~NSStreamEventHasBytesAvailable;
|
2006-03-01 20:03:35 +00:00
|
|
|
if (acceptReturn == INVALID_SOCKET)
|
|
|
|
{
|
|
|
|
errno = WSAGetLastError();// test for real error
|
|
|
|
if (errno != WSAEWOULDBLOCK && errno != WSAECONNRESET &&
|
|
|
|
errno != WSAEINPROGRESS && errno != WSAEINTR)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
ins = nil;
|
|
|
|
outs = nil;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2006-08-10 05:50:08 +00:00
|
|
|
/*
|
|
|
|
* Windows only permits a single event to be associated with a socket
|
|
|
|
* at any time, but the runloop system only allows an event handle to
|
|
|
|
* be added to the loop once, and we have two streams.
|
|
|
|
* So we create two events, one for each stream, so that we can have
|
|
|
|
* both streams scheduled in the run loop, but we make sure that the
|
|
|
|
* _dispatch method in each stream actually handles things for both
|
|
|
|
* streams so that whichever stream gets signalled, the correct
|
|
|
|
* actions are taken.
|
|
|
|
*/
|
|
|
|
WSAEVENT ievent = CreateEvent(NULL, NO, NO, NULL);
|
|
|
|
WSAEVENT oevent = CreateEvent(NULL, NO, NO, NULL);
|
2006-03-01 20:03:35 +00:00
|
|
|
// no need to connect again
|
|
|
|
[ins setPassive: YES];
|
|
|
|
[outs setPassive: YES];
|
|
|
|
// copy the addr to outs
|
|
|
|
memcpy([outs peerAddr], [ins peerAddr], len);
|
2006-03-21 16:22:42 +00:00
|
|
|
[ins setSock: acceptReturn];
|
|
|
|
[outs setSock: acceptReturn];
|
2006-08-10 05:50:08 +00:00
|
|
|
[ins setEvent: ievent];
|
|
|
|
[outs setEvent: oevent];
|
2006-03-01 20:03:35 +00:00
|
|
|
}
|
|
|
|
if (inputStream)
|
|
|
|
{
|
|
|
|
[ins setSibling: outs];
|
|
|
|
*inputStream = ins;
|
|
|
|
}
|
|
|
|
if (outputStream)
|
|
|
|
{
|
|
|
|
[outs setSibling: ins];
|
|
|
|
*outputStream = outs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-03-21 15:33:05 +00:00
|
|
|
- (void) _dispatch
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
WSANETWORKEVENTS events;
|
2006-03-01 20:03:35 +00:00
|
|
|
|
2006-08-08 13:31:50 +00:00
|
|
|
if (WSAEnumNetworkEvents(_sock, _loopID, &events) == SOCKET_ERROR)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
|
|
|
errno = WSAGetLastError();
|
|
|
|
[self _recordError];
|
|
|
|
[self _sendEvent: NSStreamEventErrorOccurred];
|
|
|
|
}
|
2006-08-08 13:31:50 +00:00
|
|
|
else if (events.lNetworkEvents & FD_ACCEPT)
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
2006-08-08 13:31:50 +00:00
|
|
|
events.lNetworkEvents ^= FD_ACCEPT;
|
2006-03-01 20:03:35 +00:00
|
|
|
[self _setStatus: NSStreamStatusReading];
|
|
|
|
[self _sendEvent: NSStreamEventHasBytesAvailable];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@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
|
|
|
|
{
|
|
|
|
const char *addr_c = [addr cStringUsingEncoding: NSUTF8StringEncoding];
|
|
|
|
|
|
|
|
[super init];
|
|
|
|
_serverAddr.sin_family = AF_INET;
|
|
|
|
_serverAddr.sin_port = htons(port);
|
|
|
|
_serverAddr.sin_addr.s_addr = inet_addr(addr_c);
|
2006-03-21 16:22:42 +00:00
|
|
|
_sock = socket(AF_INET, SOCK_STREAM, 0);
|
2006-03-21 15:33:05 +00:00
|
|
|
if (_serverAddr.sin_addr.s_addr == INADDR_NONE || _loopID < 0) // error
|
2006-03-01 20:03:35 +00:00
|
|
|
{
|
|
|
|
RELEASE(self);
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
2006-08-10 20:39:33 +00:00
|
|
|
|
|
|
|
@implementation GSLocalServerStream
|
|
|
|
|
|
|
|
- (id) init
|
|
|
|
{
|
|
|
|
DESTROY(self);
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initToAddr: (NSString*)addr
|
|
|
|
{
|
|
|
|
if ((self = [super init]) != nil)
|
|
|
|
{
|
|
|
|
path = RETAIN([@"\\\\.\\pipe\\" stringByAppendingString: addr]);
|
|
|
|
_loopID = INVALID_HANDLE_VALUE;
|
|
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
if ([self _isOpened])
|
|
|
|
{
|
|
|
|
[self close];
|
|
|
|
}
|
|
|
|
RELEASE(path);
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) open
|
|
|
|
{
|
|
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
|
|
BOOL alreadyConnected = NO;
|
|
|
|
|
|
|
|
NSAssert(handle == INVALID_HANDLE_VALUE, NSInternalInconsistencyException);
|
|
|
|
|
|
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
|
|
saAttr.bInheritHandle = TRUE;
|
|
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
|
|
|
|
|
|
handle = CreateNamedPipeW([path fileSystemRepresentation],
|
|
|
|
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
|
|
|
|
PIPE_TYPE_BYTE,
|
|
|
|
PIPE_UNLIMITED_INSTANCES,
|
|
|
|
BUFSIZ*64,
|
|
|
|
BUFSIZ*64,
|
|
|
|
100000,
|
|
|
|
&saAttr);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([self _loopID] == INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
/* No existing event to use ..,. create a new one.
|
|
|
|
*/
|
|
|
|
[self _setLoopID: CreateEvent(NULL, NO, NO, NULL)];
|
|
|
|
}
|
|
|
|
ov.Offset = 0;
|
|
|
|
ov.OffsetHigh = 0;
|
|
|
|
ov.hEvent = [self _loopID];
|
|
|
|
if (ConnectNamedPipe(handle, &ov) == 0)
|
|
|
|
{
|
|
|
|
errno = GetLastError();
|
|
|
|
if (errno == ERROR_PIPE_CONNECTED)
|
|
|
|
{
|
|
|
|
alreadyConnected = YES;
|
|
|
|
}
|
|
|
|
else if (errno != ERROR_IO_PENDING)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ([self _isOpened] == NO)
|
|
|
|
{
|
|
|
|
[super open];
|
|
|
|
}
|
|
|
|
if (alreadyConnected == YES)
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
|
|
|
[self _sendEvent: NSStreamEventHasBytesAvailable];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) close
|
|
|
|
{
|
|
|
|
if (_loopID != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
CloseHandle((HANDLE)_loopID);
|
|
|
|
}
|
|
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
CancelIo(handle);
|
|
|
|
if (CloseHandle(handle) == 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
}
|
|
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
[super close];
|
|
|
|
_loopID = INVALID_HANDLE_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) acceptWithInputStream: (NSInputStream **)inputStream
|
|
|
|
outputStream: (NSOutputStream **)outputStream
|
|
|
|
{
|
|
|
|
GSPipeInputStream *ins = nil;
|
|
|
|
GSPipeOutputStream *outs = nil;
|
|
|
|
|
|
|
|
_events &= ~NSStreamEventHasBytesAvailable;
|
|
|
|
|
|
|
|
// the type of the stream does not matter, since we are only using the fd
|
|
|
|
ins = AUTORELEASE([GSPipeInputStream new]);
|
|
|
|
outs = AUTORELEASE([GSPipeOutputStream new]);
|
|
|
|
|
|
|
|
[ins _setHandle: handle];
|
|
|
|
[outs _setHandle: handle];
|
|
|
|
|
|
|
|
handle = INVALID_HANDLE_VALUE;
|
|
|
|
[self open]; // Re-open to accept more
|
|
|
|
|
|
|
|
if (inputStream)
|
|
|
|
{
|
|
|
|
[ins setSibling: outs];
|
|
|
|
*inputStream = ins;
|
|
|
|
}
|
|
|
|
if (outputStream)
|
|
|
|
{
|
|
|
|
[outs setSibling: ins];
|
|
|
|
*outputStream = outs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _dispatch
|
|
|
|
{
|
|
|
|
DWORD size;
|
|
|
|
|
|
|
|
if (GetOverlappedResult(handle, &ov, &size, TRUE) == 0)
|
|
|
|
{
|
|
|
|
[self _recordError];
|
|
|
|
[self _sendEvent: NSStreamEventErrorOccurred];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self _setStatus: NSStreamStatusOpen];
|
|
|
|
[self _sendEvent: NSStreamEventHasBytesAvailable];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|