/ep-020/test_longpress_pinch/build/iphone/Classes/TiNetworkTCPSocketProxy.mm
Objective C++ | 761 lines | 604 code | 136 blank | 21 comment | 100 complexity | 3c2c00be19f350656b0d1b3eda75db2b MD5 | raw file
Possible License(s): Apache-2.0
- /**
- * Appcelerator Titanium Mobile
- * Copyright (c) 2009-2012 by Appcelerator, Inc. All Rights Reserved.
- * Licensed under the terms of the Apache Public License
- * Please see the LICENSE included with this distribution for details.
- *
- * WARNING: This is generated code. Modify at your own risk and without support.
- */
- #ifdef USE_TI_NETWORK
- #import "TiNetworkTCPSocketProxy.h"
- #import <sys/socket.h>
- #import <netinet/in.h>
- #import <netdb.h>
- #include <CFNetwork/CFSocketStream.h>
- extern NSString* const INADDR_ANY_token;
- #pragma mark Forward declarations
- // Size of the read buffer; ideally should be a multiple of 1024 (1k), up to 4096 (4k, page size)
- const NSUInteger bufferSize = 4096;
- // TODO: Add host information for better error reporting
- typedef struct {
- CFReadStreamRef inputStream;
- CFWriteStreamRef outputStream;
-
- NSRecursiveLock* writeLock;
- NSMutableArray* writeBuffer;
- NSUInteger bufferPos;
- } SocketStreams;
- void handleSocketConnection(CFSocketRef socket, CFSocketCallBackType type,
- CFDataRef address, const void* data, void* info);
- void handleReadData(CFReadStreamRef input,
- CFStreamEventType event,
- void* info);
- void handleWriteData(CFWriteStreamRef input,
- CFStreamEventType event,
- void* info);
- const CFOptionFlags readStreamEventFlags =
- kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered | kCFStreamEventOpenCompleted;
- const CFOptionFlags writeStreamEventFlags =
- kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventOpenCompleted;
- @implementation TiNetworkTCPSocketProxy
- #pragma mark Macros
- #define VALID (socket!=NULL) && [[self isValid] boolValue]
- #pragma mark Private
- -(void)closeRemoteSocket:(CFSocketNativeHandle)remoteSocket
- {
- NSNumber* remoteSocketObject = [NSNumber numberWithInt:remoteSocket];
- NSData* socketStreamsObject = [remoteSocketDictionary objectForKey:remoteSocketObject];
- SocketStreams* streams = (SocketStreams*)[socketStreamsObject bytes];
-
- if (streams->inputStream) {
- if (!(CFReadStreamGetStatus(streams->inputStream) == kCFStreamStatusClosed)) {
- CFReadStreamClose(streams->inputStream);
- }
- CFRelease(streams->inputStream);
- }
- if (streams->outputStream) {
- if (!(CFWriteStreamGetStatus(streams->outputStream) == kCFStreamStatusClosed)) {
- CFWriteStreamClose(streams->outputStream);
- }
- CFRelease(streams->outputStream);
- }
-
- [streams->writeBuffer release];
- [streams->writeLock release];
-
- [remoteSocketDictionary removeObjectForKey:remoteSocketObject];
- }
- -(CFDataRef)createAddressData
- {
- struct sockaddr_in address;
-
- memset(&address, 0, sizeof(address)); // THIS is the finnicky bit: sockaddr_in needs to have 8 bytes of 0 at the end to be compatible with sockaddr
- address.sin_len = sizeof(address);
- address.sin_port = htons(port);
- address.sin_family = AF_INET;
-
- if ([hostName isEqual:INADDR_ANY_token]) {
- address.sin_addr.s_addr = htonl(INADDR_ANY);
- }
- else {
- struct hostent *host;
- host = gethostbyname([hostName cStringUsingEncoding:[NSString defaultCStringEncoding]]);
- if (host == NULL) {
- if (socket) {
- CFSocketInvalidate(socket);
- CFRelease(socket);
- socket = NULL;
- }
-
- [self throwException:[NSString stringWithFormat:@"Couldn't resolve host %@: %d", hostName, h_errno]
- subreason:nil
- location:CODELOCATION];
- }
- memcpy(&address.sin_addr.s_addr, host->h_addr_list[0], host->h_length);
- }
-
- return CFDataCreate(kCFAllocatorDefault,
- (UInt8*)&address,
- sizeof(address));
- }
- -(void)configureSocketForHandle:(CFSocketNativeHandle)fd
- {
- if (socket) {
- return; // Socket already configured, either by listener or previous action
- }
-
- socket = CFSocketCreateWithNative(NULL,
- fd,
- kCFSocketNoCallBack,
- NULL,
- NULL);
-
- CFSocketSetSocketFlags(socket, CFSocketGetSocketFlags(socket) & ~kCFSocketCloseOnInvalidate);
- }
- -(void)handleError:(NSStream*)stream
- {
- NSError* error = [stream streamError];
- NSString* event = ([stream isKindOfClass:[NSInputStream class]]) ? @"readError" : @"writeError";
-
- [stream close];
-
- [configureCondition lock];
- [configureCondition signal];
- [configureCondition unlock];
-
- [self fireEvent:event
- withObject:[NSDictionary dictionaryWithObjectsAndKeys:[error localizedDescription], @"error",
- [NSNumber numberWithInt:[error code]], @"code",
- nil]];
- }
- -(CFSocketNativeHandle)getHandleFromStream:(NSStream*)stream
- {
- CFSocketNativeHandle remoteSocket;
- CFDataRef remoteSocketData;
-
- if ([stream isKindOfClass:[NSInputStream class]]) {
- remoteSocketData = (CFDataRef)CFReadStreamCopyProperty((CFReadStreamRef)stream, kCFStreamPropertySocketNativeHandle);
- }
- else {
- remoteSocketData = (CFDataRef)CFWriteStreamCopyProperty((CFWriteStreamRef)stream, kCFStreamPropertySocketNativeHandle);
- }
-
- if (remoteSocketData == NULL) {
- return -1;
- }
-
- CFDataGetBytes(remoteSocketData, CFRangeMake(0, CFDataGetLength(remoteSocketData)), (UInt8*)&remoteSocket);
- CFRelease(remoteSocketData);
-
- return remoteSocket;
- }
- -(void)initializeReadStream:(NSInputStream*)input
- {
- CFSocketNativeHandle remoteSocket = [self getHandleFromStream:input];
- if (remoteSocket == -1) {
- [self handleError:input];
- return;
- }
-
- SocketStreams* streams =
- (SocketStreams*)[[remoteSocketDictionary objectForKey:[NSNumber numberWithInt:remoteSocket]] bytes];
-
- if (!streams) {
- streams = (SocketStreams*)malloc(sizeof(SocketStreams));
- streams->outputStream = NULL;
- streams->writeBuffer = nil;
- streams->writeLock = nil;
-
- [remoteSocketDictionary setObject:[NSData dataWithBytesNoCopy:streams length:sizeof(SocketStreams)]
- forKey:[NSNumber numberWithInt:remoteSocket]];
- }
-
- streams->inputStream = (CFReadStreamRef)input;
-
- CFReadStreamSetProperty((CFReadStreamRef)input, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
- [self configureSocketForHandle:remoteSocket];
-
- if (mode && WRITE_MODE) {
- if (streams->outputStream) {
- [configureCondition lock];
- [configureCondition signal];
- [configureCondition unlock];
- }
- }
- else {
- [configureCondition lock];
- [configureCondition signal];
- [configureCondition unlock];
- }
- }
- -(void)initializeWriteStream:(NSOutputStream*)output
- {
- CFSocketNativeHandle remoteSocket = [self getHandleFromStream:output];
- if (remoteSocket == -1) {
- [self handleError:output];
- return;
- }
-
- SocketStreams* streams =
- (SocketStreams*)[[remoteSocketDictionary objectForKey:[NSNumber numberWithInt:remoteSocket]] bytes];
-
- if (!streams) {
- streams = (SocketStreams*)malloc(sizeof(SocketStreams));
- streams->inputStream = NULL;
-
- [remoteSocketDictionary setObject:[NSData dataWithBytesNoCopy:streams length:sizeof(SocketStreams)]
- forKey:[NSNumber numberWithInt:remoteSocket]];
- }
-
- streams->outputStream = (CFWriteStreamRef)output;
- streams->writeBuffer = [[NSMutableArray alloc] init];
- streams->writeLock = [[NSRecursiveLock alloc] init];
- streams->bufferPos = 0;
-
- CFWriteStreamSetProperty((CFWriteStreamRef)output, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
- [self configureSocketForHandle:remoteSocket];
-
- if (mode && READ_MODE) {
- if (streams->inputStream) {
- [configureCondition lock];
- [configureCondition signal];
- [configureCondition unlock];
- }
- }
- else {
- [configureCondition lock];
- [configureCondition signal];
- [configureCondition unlock];
- }
- }
- -(void)readFromStream:(NSInputStream*)input
- {
- CFSocketNativeHandle remoteSocket = [self getHandleFromStream:input];
- if (remoteSocket == -1) {
- [self handleError:input];
- return;
- }
-
- // We can't just use -[NSInputStream getBuffer:length:] because the input stream is secretly
- // based on sockets, according to the CFReadStream it comes from.
-
- // NOTE: Without sentenels, this could result in some weird behavior (for example, if two images are transmitted back-to-back with no break).
- NSMutableData* data = [[[NSMutableData alloc] init] autorelease];
- while ([input hasBytesAvailable]) {
- uint8_t* buffer = (uint8_t*)malloc(bufferSize * sizeof(uint8_t));
- NSInteger bytesRead = [input read:buffer maxLength:bufferSize];
- // Not clear whether the failure condition is 0 or -1 from documentation
- if (bytesRead == 0 || bytesRead == -1) {
- free(buffer);
- [self handleError:input];
- return;
- }
- [data appendBytes:buffer length:bytesRead];
- free(buffer);
- }
- TiBlob* dataBlob = [[[TiBlob alloc] initWithData:data mimetype:@"application/octet-stream"] autorelease];
- [self fireEvent:@"read" withObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:remoteSocket], @"from",
- dataBlob, @"data",
- nil]];
- }
- -(void)writeToStream:(NSOutputStream*)output
- {
- CFSocketNativeHandle remoteSocket = [self getHandleFromStream:output];
- if (remoteSocket == -1) {
- [self handleError:output];
- return;
- }
-
- SocketStreams* streams =
- (SocketStreams*)[[remoteSocketDictionary objectForKey:[NSNumber numberWithInt:remoteSocket]] bytes];
-
- [streams->writeLock lock];
-
- if ([streams->writeBuffer count] == 0) {
- [streams->writeLock unlock];
- return;
- }
-
- do {
- NSInteger wroteBytes = 0;
- NSData* data = [streams->writeBuffer objectAtIndex:0];
-
- const uint8_t* startPos = (const uint8_t*)[data bytes] + streams->bufferPos;
- NSUInteger length = [data length] - streams->bufferPos;
-
- wroteBytes = [output write:startPos maxLength:length];
-
- if (wroteBytes == -1) {
- [self handleError:output];
- [streams->writeLock unlock];
- break;
- }
-
- if (wroteBytes != length) {
- streams->bufferPos += wroteBytes;
- }
- else {
- [streams->writeBuffer removeObjectAtIndex:0];
- streams->bufferPos = 0;
- }
- } while ([output hasSpaceAvailable] &&
- streams->bufferPos == 0 &&
- [streams->writeBuffer count] > 0);
-
- [streams->writeLock unlock];
- }
- #pragma mark Public
- -(void)_configure
- {
- [super _configure];
-
- socket = NULL;
- remoteSocketDictionary = [[NSMutableDictionary alloc] init];
- configureCondition = [[NSCondition alloc] init];
- mode = READ_WRITE_MODE;
- }
- -(void)_destroy
- {
- if (VALID) {
- [self close:nil];
- }
-
- RELEASE_TO_NIL(hostName);
- RELEASE_TO_NIL(remoteSocketDictionary);
- RELEASE_TO_NIL(configureCondition);
-
- [super _destroy];
- }
- -(NSNumber*)mode
- {
- return [NSNumber numberWithInt:mode];
- }
- -(void)setMode:(NSNumber*)mode_
- {
- switch ([mode_ intValue]) {
- case READ_MODE:
- case WRITE_MODE:
- case READ_WRITE_MODE:
- mode = (SocketMode)[mode_ intValue];
- break;
- default:
- [self throwException:TiExceptionRangeError
- subreason:@"Invalid socket mode"
- location:CODELOCATION];
- break;
- }
- }
- -(NSString*)hostName
- {
- return hostName;
- }
- -(void)setHostName:(NSString*)hostName_
- {
- if (hostName == hostName_) {
- return;
- }
- [hostName release];
- hostName = [hostName_ retain];
- }
- -(NSNumber*)port
- {
- return [NSNumber numberWithInt:port];
- }
- -(void)setPort:(NSNumber*)port_
- {
- port = [port_ intValue];
- }
- -(void)setStripTerminator:(NSNumber *)stripTerminator_
- {
- stripTerminator = [TiUtils boolValue:stripTerminator_ def:NO];
- }
- -(NSNumber*)stripTerminator
- {
- return NUMBOOL(stripTerminator);
- }
- -(NSNumber*)isValid
- {
- if (socket!=NULL) {
- return [NSNumber numberWithBool:CFSocketIsValid(socket)];
- }
- return [NSNumber numberWithBool:false];
- }
- -(void)listen:(id)unused
- {
- if (VALID) {
- [self throwException:@"Socket already opened"
- subreason:nil
- location:CODELOCATION];
- }
-
- if (hostName == nil) {
- [self throwException:@"Host is null"
- subreason:nil
- location:CODELOCATION];
- }
-
- CFSocketContext socketContext;
- socketContext.version = 0;
- socketContext.info = self;
- socketContext.retain = NULL;
- socketContext.release = NULL;
- socketContext.copyDescription = NULL;
-
- // SocketContext is copied
- socket = CFSocketCreate(kCFAllocatorDefault,
- PF_INET,
- SOCK_STREAM,
- IPPROTO_TCP,
- kCFSocketAcceptCallBack,
- handleSocketConnection,
- &socketContext);
-
- if (!socket) {
- [self throwException:[NSString stringWithFormat:@"Failed to create socket: %d", errno]
- subreason:nil
- location:CODELOCATION];
- }
-
- CFDataRef addressData = (CFDataRef)[self createAddressData];
-
- int reuseOn = 1;
- setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, &reuseOn, sizeof(reuseOn));
- setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEPORT, &reuseOn, sizeof(reuseOn));
-
- CFSocketError sockError = CFSocketSetAddress(socket,
- addressData);
- switch (sockError) {
- case kCFSocketError: {
- CFSocketInvalidate(socket);
- CFRelease(socket);
- socket = NULL;
-
- CFRelease(addressData);
-
- [self throwException:[NSString stringWithFormat:@"Failed to listen on %@:%d: %d", hostName, port, errno]
- subreason:nil
- location:CODELOCATION];
- break;
- }
- }
-
- CFRelease(addressData);
-
- socketRunLoop = CFSocketCreateRunLoopSource(kCFAllocatorDefault,
- socket,
- 1);
- CFRunLoopAddSource(CFRunLoopGetMain(),
- socketRunLoop,
- kCFRunLoopCommonModes);
- }
- -(void)connect:(id)unused
- {
- if (VALID) {
- [self throwException:@"Socket already opened"
- subreason:nil
- location:CODELOCATION];
- }
-
- if (hostName == nil) {
- [self throwException:@"Host is null"
- subreason:nil
- location:CODELOCATION];
- }
-
- CFSocketSignature signature;
- signature.protocolFamily = PF_INET;
- signature.socketType = SOCK_STREAM;
- signature.protocol = IPPROTO_TCP;
- signature.address = [self createAddressData]; // Follows create rule; clean up later
-
-
- CFReadStreamRef inputStream;
- CFWriteStreamRef outputStream;
-
- CFStreamCreatePairWithPeerSocketSignature(NULL,
- &signature,
- (mode & READ_MODE) ? &inputStream : NULL,
- (mode & WRITE_MODE) ? &outputStream : NULL);
-
- CFStreamClientContext context;
- context.version = 0;
- context.info = self;
- context.retain = NULL;
- context.release = NULL;
- context.copyDescription = NULL;
-
- // TODO: Do we catch errors in the stream opening because the stream FD will be NULL in the callback?
- if (mode & READ_MODE) {
- CFReadStreamSetClient(inputStream, readStreamEventFlags | kCFStreamEventOpenCompleted, handleReadData, &context);
- CFReadStreamScheduleWithRunLoop(inputStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- CFReadStreamOpen(inputStream);
- }
-
- if (mode & WRITE_MODE) {
- CFWriteStreamSetClient(outputStream, writeStreamEventFlags | kCFStreamEventOpenCompleted, handleWriteData, &context);
- CFWriteStreamScheduleWithRunLoop(outputStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- CFWriteStreamOpen(outputStream);
- }
-
- CFRelease(signature.address);
-
- if (!VALID &&
- !(inputStream && (CFReadStreamGetStatus(inputStream) == kCFStreamStatusError)) &&
- !(outputStream && (CFWriteStreamGetStatus(outputStream) == kCFStreamStatusError))) {
-
- [configureCondition lock];
- [configureCondition wait];
- [configureCondition unlock];
- }
- }
- -(void)close:(id)unused
- {
- @synchronized(self)
- {
- if (!VALID) {
- [self throwException:@"Socket is not open"
- subreason:nil
- location:CODELOCATION];
- }
-
- if (socketRunLoop!=NULL)
- {
- CFRunLoopRemoveSource(CFRunLoopGetMain(),
- socketRunLoop,
- kCFRunLoopCommonModes);
- CFRelease(socketRunLoop);
- socketRunLoop=NULL;
- }
-
- NSEnumerator* keys = [[remoteSocketDictionary allKeys] objectEnumerator];
- id remoteSocketObject;
-
- // Shut down all of the streams and remote connections
- while ((remoteSocketObject = [keys nextObject]))
- {
- CFSocketNativeHandle remoteSocket = [remoteSocketObject intValue];
- [self closeRemoteSocket:remoteSocket];
- }
-
- if (socket!=NULL)
- {
- CFSocketInvalidate(socket);
- CFRelease(socket);
- }
-
- socket = NULL;
- }
- }
- -(void)write:(id)args;
- {
- if (!(mode & WRITE_MODE)) {
- [self throwException:@"Socket does not support writing"
- subreason:nil
- location:CODELOCATION];
- }
- else if (!VALID) {
- [self throwException:@"Socket is invalid"
- subreason:nil
- location:CODELOCATION];
- }
-
- NSData* data = nil;
-
- id arg = [args objectAtIndex:0];
- if ([arg isKindOfClass:[TiBlob class]]) {
- data = [arg data];
- }
- else if ([arg isKindOfClass:[NSString class]]) {
- NSUInteger length = (stripTerminator) ? [arg length] : [arg length] + 1;
- data = [NSData dataWithBytes:[arg UTF8String] length:length];
- }
- else {
- NSString* errorStr = [NSString stringWithFormat:@"expected: %@ or %@, was: %@", [TiBlob class], [NSString class], [arg class]];
- THROW_INVALID_ARG(errorStr)
- }
-
- NSNumber* key = nil;
- NSEnumerator* keyEnum = [[remoteSocketDictionary allKeys] objectEnumerator];
- BOOL broadcast = YES;
- if ([args count] > 1) {
- ENSURE_CLASS([args objectAtIndex:1], [NSNumber class])
- key = [args objectAtIndex:1];
- broadcast = NO;
- }
- else {
- key = [keyEnum nextObject];
- // Short-circut to avoid degenerate case where there are 0 sockets attached to a listener
- if (key == nil) {
- return;
- }
- }
-
- do {
- NSData* streamData = [remoteSocketDictionary objectForKey:key];
- if (streamData == nil) {
- [self throwException:[NSString stringWithFormat:@"Invalid socket descriptor (%@)", key]
- subreason:nil
- location:CODELOCATION];
- }
-
- SocketStreams* streams = (SocketStreams*)[streamData bytes];
-
- if (streams->writeBuffer == nil) {
- [configureCondition lock];
- [configureCondition wait];
- [configureCondition unlock];
- }
-
- [streams->writeLock lock];
- [streams->writeBuffer addObject:data];
-
- if (CFWriteStreamCanAcceptBytes(streams->outputStream)) {
- [self writeToStream:(NSOutputStream*)(streams->outputStream)];
- }
- [streams->writeLock unlock];
- } while (broadcast && (key = [keyEnum nextObject]));
- }
- @end
- #pragma mark CFSocket/CFStream data handling
- void handleSocketConnection(CFSocketRef socket, CFSocketCallBackType type,
- CFDataRef address, const void* data, void* info) {
- switch (type) {
- case kCFSocketAcceptCallBack: {
- TiNetworkTCPSocketProxy* hostSocket = (TiNetworkTCPSocketProxy*)info;
- CFSocketNativeHandle sock = *(CFSocketNativeHandle*)data;
-
- CFReadStreamRef inputStream;
- CFWriteStreamRef outputStream;
-
- SocketMode mode = (SocketMode)[[hostSocket mode] intValue];
- CFStreamCreatePairWithSocket(kCFAllocatorDefault,
- sock,
- (mode & READ_MODE) ? &inputStream : NULL,
- (mode & WRITE_MODE) ? &outputStream : NULL);
-
- CFStreamClientContext context;
- context.version = 0;
- context.info = hostSocket;
- context.retain = NULL;
- context.release = NULL;
- context.copyDescription = NULL;
-
- if (mode & READ_MODE) {
- CFReadStreamSetClient(inputStream, readStreamEventFlags, handleReadData, &context);
- CFReadStreamScheduleWithRunLoop(inputStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- CFReadStreamOpen(inputStream);
- }
-
- if (mode & WRITE_MODE) {
- CFWriteStreamSetClient(outputStream, writeStreamEventFlags, handleWriteData, &context);
- CFWriteStreamScheduleWithRunLoop(outputStream, CFRunLoopGetMain(), kCFRunLoopCommonModes);
- CFWriteStreamOpen(outputStream);
- }
-
- break;
- }
- }
- }
- void handleReadData(CFReadStreamRef input,
- CFStreamEventType event,
- void* info)
- {
- TiNetworkTCPSocketProxy* hostSocket = (TiNetworkTCPSocketProxy*)info;
-
- switch (event) {
- case kCFStreamEventOpenCompleted: {
- [hostSocket initializeReadStream:(NSInputStream*)input];
- break;
- }
- case kCFStreamEventEndEncountered: {
- CFSocketNativeHandle remoteSocket = [hostSocket getHandleFromStream:(NSInputStream*)input];
- if (remoteSocket != -1) {
- [hostSocket closeRemoteSocket:remoteSocket];
- }
- break;
- }
- // There's not very much information you can get out of an error like this, other than
- // that it occurred. It's not recoverable without the direct stream information, most likely.
- case kCFStreamEventErrorOccurred: {
- [hostSocket handleError:(NSInputStream*)input];
- break;
- }
- // This event is NOT necessarily fired until all current available data has been read. Gotta clear that buffer first!
- case kCFStreamEventHasBytesAvailable: {
- [hostSocket readFromStream:(NSInputStream*)input];
- break;
- }
- }
- }
- void handleWriteData(CFWriteStreamRef output,
- CFStreamEventType event,
- void* info)
- {
- TiNetworkTCPSocketProxy* hostSocket = (TiNetworkTCPSocketProxy*)info;
-
- switch (event) {
- case kCFStreamEventOpenCompleted: {
- [hostSocket initializeWriteStream:(NSOutputStream*)output];
- break;
- }
- case kCFStreamEventErrorOccurred: {
- [hostSocket handleError:(NSOutputStream*)output];
- break;
- }
- case kCFStreamEventCanAcceptBytes: {
- [hostSocket writeToStream:(NSOutputStream*)output];
- break;
- }
- }
- }
- #endif