/ep-020/test_longpress_pinch/build/iphone/Classes/TiNetworkSocketTCPProxy.m
Objective C | 834 lines | 634 code | 131 blank | 69 comment | 105 complexity | 1e191b251ca6fec38f310f9cc749e21f MD5 | raw file
Possible License(s): Apache-2.0
- /**
- * Appcelerator Titanium Mobile
- * Copyright (c) 2009-2011 by test_longpress_pinch, 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_NETWORKSOCKET
- #import "TiNetworkSocketTCPProxy.h"
- #import "NetworkModule.h"
- #import "TiBlob.h"
- #import "TiBuffer.h"
- static NSString* SOCK_KEY = @"socket";
- static NSString* ARG_KEY = @"arg";
- @interface TiNetworkSocketTCPProxy (Private)
- -(void)cleanupSocket;
- -(void)setConnectedSocket:(AsyncSocket*)sock;
- -(void)startConnectingSocket;
- -(void)startListeningSocket;
- -(void)startAcceptedSocket:(NSDictionary*)info;
- -(void)socketRunLoop;
- @end
- @implementation TiNetworkSocketTCPProxy
- @synthesize host, connected, accepted, closed, error;
- #pragma mark Internals
- -(id)init
- {
- if (self = [super init]) {
- listening = [[NSCondition alloc] init];
- ioCondition = [[NSCondition alloc] init];
- internalState = SOCKET_INITIALIZED;
- socketThread = nil;
- acceptRunLoop = nil;
- socketReady = NO;
- acceptArgs = [[NSMutableDictionary alloc] init];
- readyCondition = [[NSCondition alloc] init];
- operationInfo = [[NSMutableDictionary alloc] init];
- asynchTagCount = 0;
- }
- return self;
- }
- -(void)dealloc
- {
- // Calls _destroy
- [super dealloc];
- }
- -(void)_destroy
- {
- internalState = SOCKET_CLOSED;
- // Socket cleanup, if necessary
- if ([socketThread isExecuting]) {
- [self performSelector:@selector(cleanupSocket) onThread:socketThread withObject:nil waitUntilDone:YES];
- }
-
- RELEASE_TO_NIL(operationInfo);
- RELEASE_TO_NIL(acceptArgs);
- RELEASE_TO_NIL(socketError);
-
- RELEASE_TO_NIL(connected);
- RELEASE_TO_NIL(accepted);
- RELEASE_TO_NIL(closed);
- RELEASE_TO_NIL(error);
-
- // Release the conditions... so long as each condition has been retained, this is safe.
- RELEASE_TO_NIL(listening);
- RELEASE_TO_NIL(ioCondition);
- RELEASE_TO_NIL(readyCondition);
-
- RELEASE_TO_NIL(host);
-
- [super _destroy];
- }
- -(void)cleanupSocket
- {
- // Signal anything that's still waiting on the socket, just to be safe
- [listening lock];
- [listening broadcast];
- [listening unlock];
-
- [ioCondition lock];
- [ioCondition broadcast];
- [ioCondition unlock];
-
- [readyCondition lock];
- [readyCondition broadcast];
- [readyCondition unlock];
-
- [socket disconnect];
- [socket setDelegate:nil];
-
- RELEASE_TO_NIL(socket);
- }
- -(void)setConnectedSocket:(AsyncSocket*)sock
- {
- [self cleanupSocket];
- internalState = SOCKET_CONNECTED;
- socket = [sock retain];
- [socket setDelegate:self];
- socketThread = [NSThread currentThread];
- }
- #define AUTORELEASE_LOOP 5
- -(void)socketRunLoop
- {
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- [socketThread setName:[NSString stringWithFormat:@"Ti.Network.Socket.TCP (%x)",self]];
- // Begin the run loop for the socket
- int counter=0;
- while (!(internalState & (SOCKET_CLOSED | SOCKET_ERROR)) &&
- [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
- {
- if (++counter == AUTORELEASE_LOOP) {
- [pool release];
- pool = [[NSAutoreleasePool alloc] init];
- counter = 0;
- }
- }
- // No longer send messages to this thread!
- socketThread = nil;
-
- [pool release];
- }
- -(void)startConnectingSocket;
- {
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-
- id port = [self valueForUndefinedKey:@"port"]; // Can be string or int
- NSNumber* timeout = [self valueForUndefinedKey:@"timeout"];
-
- // TODO: We're on a BG thread... need to make sure this gets caught somehow.
-
- socket = [[AsyncSocket alloc] initWithDelegate:self];
- socketThread = [NSThread currentThread];
-
- NSError* err = nil;
- BOOL success;
- if (timeout == nil) {
- success = [socket connectToHost:host onPort:[TiUtils intValue:port] error:&err];
- }
- else {
- success = [socket connectToHost:host onPort:[port intValue] withTimeout:[timeout doubleValue] error:&err];
- }
-
- if (err || !success) {
- internalState = SOCKET_ERROR;
- [self cleanupSocket];
-
- if (error != nil) {
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket",[err localizedDescription],@"error",NUMINT([err code]),@"errorCode", nil];
- [self _fireEventToListener:@"error" withObject:event listener:error thisObject:self];
- }
-
- socketThread = nil;
- [pool release];
- return;
- }
-
- [self socketRunLoop];
-
- [pool release];
- }
- -(void)startListeningSocket;
- {
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
-
- id port = [self valueForKey:@"port"]; // Can be string or int
-
- socket = [[AsyncSocket alloc] initWithDelegate:self];
-
- NSError* err = nil;
- BOOL success = [socket acceptOnInterface:host port:[port intValue] autoaccept:NO error:&err];
-
- if (err || !success) {
- internalState = SOCKET_ERROR;
- socketError = [err retain];
- [self cleanupSocket]; // Cleanup signals the condition
-
- [pool release];
- return;
- }
- // Make sure that we 'accept' only when explicitly requested
- CFSocketRef cfSocket = [socket getCFSocket];
- CFOptionFlags options = CFSocketGetSocketFlags(cfSocket);
- CFSocketSetSocketFlags(cfSocket, options & ~kCFSocketAutomaticallyReenableAcceptCallBack);
-
- socketThread = [NSThread currentThread];
-
- [listening lock];
- internalState = SOCKET_LISTENING;
- [listening signal];
- [listening unlock];
-
- [self socketRunLoop];
-
- [pool release];
- }
- -(void)startAcceptedSocket:(NSDictionary*)info
- {
- NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
- // Here's a goofy pattern in sockets; because we may _destroy on the Kroll thread while other threads (i.e. sockets, or newly created thread)
- // is blocking, we need to hold on to the conditions we wait on, and then release them once we're done with them. Introduces some extra
- // overhead, but it probably prevents DUMB CRASHES from happening.
-
- // 1. Provide the run loop to the new socket...
- NSCondition* tempConditionRef = [readyCondition retain];
- [tempConditionRef lock];
- acceptRunLoop = [NSRunLoop currentRunLoop];
- [tempConditionRef signal];
- [tempConditionRef unlock];
- [tempConditionRef release];
-
- // 2. ... And then wait for it to attach to the run loop.
- AsyncSocket* newSocket = [[[info objectForKey:SOCK_KEY] retain] autorelease];
- NSArray* arg = [info objectForKey:ARG_KEY];
- TiNetworkSocketTCPProxy* proxy = [[[TiNetworkSocketTCPProxy alloc] _initWithPageContext:[self executionContext] args:arg] autorelease];
- [proxy rememberSelf];
- [proxy setConnectedSocket:newSocket];
-
- // TODO: remoteHost/remotePort & host/port?
- [proxy setValue:[newSocket connectedHost] forKey:@"host"];
- [proxy setValue:NUMINT([newSocket connectedPort]) forKey:@"port"];
-
- if (accepted != nil) {
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket",proxy,@"inbound",nil];
- [self _fireEventToListener:@"accepted" withObject:event listener:accepted thisObject:self];
- }
-
- tempConditionRef = [readyCondition retain];
- [tempConditionRef lock];
- if (!socketReady) {
- [tempConditionRef wait];
- }
- [tempConditionRef unlock];
- [tempConditionRef release];
-
- [proxy socketRunLoop];
- [proxy forgetSelf];
-
- [pool release];
- }
- #pragma mark Public API : Functions
- // Used to bump API calls onto the socket thread if necessary
- #define ENSURE_SOCKET_THREAD(f,x) \
- if (socketThread == nil) { \
- return; \
- } \
- if ([NSThread currentThread] != socketThread) { \
- [self performSelector:@selector(f:) onThread:socketThread withObject:x waitUntilDone:YES]; \
- return; \
- } \
- // Convenience for io waits
- #define SAFE_WAIT(condition, invocation) \
- {\
- NSCondition* temp = [condition retain]; \
- [temp lock]; \
- [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO]; \
- [temp wait]; \
- [temp unlock]; \
- [temp release]; \
- }\
- -(void)connect:(id)_void
- {
- if (!(internalState & SOCKET_INITIALIZED)) {
- [self throwException:[NSString stringWithFormat:@"Attempt to connect with bad state: %d",internalState]
- subreason:nil
- location:CODELOCATION];
- return;
- }
-
- if (host == nil || [host isEqual:@""]) {
- // Use the loopback
- [self setHost:@"127.0.0.1"];
- }
-
- [self performSelectorInBackground:@selector(startConnectingSocket) withObject:nil];
- }
- -(void)listen:(id)arg
- {
- if (!(internalState & SOCKET_INITIALIZED)) {
- [self throwException:[NSString stringWithFormat:@"Attempt to listen with bad state: %d", internalState]
- subreason:nil
- location:CODELOCATION];
- return;
- }
- if (host == nil || [host isEqual:@""]) {
- // Use the loopback
- [self setHost:@"127.0.0.1"];
- }
-
- [self performSelectorInBackground:@selector(startListeningSocket) withObject:nil];
-
- // Call should block until we're listening or have an error
- NSCondition* tempConditionRef = [listening retain];
- [tempConditionRef lock];
- if (!(internalState & (SOCKET_LISTENING | SOCKET_ERROR))) {
- [tempConditionRef wait];
- }
- [tempConditionRef unlock];
- [tempConditionRef release];
-
- if (internalState & SOCKET_ERROR) {
- if (socketError != nil) {
- // Have to autorelease because we need to hold onto it for throwing the exception
- RELEASE_TO_NIL_AUTORELEASE(socketError);
- [self throwException:@"TiSocketError"
- subreason:[socketError localizedDescription]
- location:CODELOCATION];
- }
- }
- }
- -(void)accept:(id)arg
- {
- // Only change the accept args if we have an accept in progress
- // TODO: Probably want a lock for this...
- if (accepting) {
- [acceptArgs setValue:arg forKey:ARG_KEY];
- return;
- }
-
- ENSURE_SOCKET_THREAD(accept,arg);
- NSDictionary* args = nil;
- ENSURE_ARG_OR_NIL_AT_INDEX(args, arg, 0, NSDictionary);
- [acceptArgs setValue:arg forKey:ARG_KEY];
-
- CFSocketRef sock = [socket getCFSocket];
- CFSocketEnableCallBacks(sock, kCFSocketAcceptCallBack);
- accepting = YES;
- }
- // TODO: Bump into 'closed' state if socketThread==nil
- -(void)close:(id)_void
- {
- // Don't switch threads until after the state check; this allows us to throw the exception on the right thread
- if (!(internalState & (SOCKET_CONNECTED | SOCKET_LISTENING | SOCKET_INITIALIZED))) {
- [self throwException:[NSString stringWithFormat:@"Attempt to close in invalid state: %d",internalState]
- subreason:nil
- location:CODELOCATION];
- return;
- }
-
- ENSURE_SOCKET_THREAD(close,_void);
-
- [self cleanupSocket];
- internalState = SOCKET_CLOSED;
- }
- #pragma mark Public API : Properties
- -(NSNumber*)state
- {
- return NUMINT(internalState);
- }
- // TODO: Move to TiBase?
- #define TYPESAFE_SETTER(funcname,prop,type) \
- -(void)funcname:(type*)val \
- { \
- ENSURE_TYPE_OR_NIL(val,type); \
- if (prop != val) { \
- [prop release]; \
- prop = [val retain]; \
- }\
- }
- TYPESAFE_SETTER(setHost, host, NSString)
- TYPESAFE_SETTER(setConnected, connected, KrollCallback)
- TYPESAFE_SETTER(setAccepted, accepted, KrollCallback)
- TYPESAFE_SETTER(setClosed, closed, KrollCallback)
- TYPESAFE_SETTER(setError, error, KrollCallback)
- #pragma mark TiStreamInternal implementations
- -(NSNumber*)isReadable:(id)_void
- {
- return NUMBOOL(internalState & SOCKET_CONNECTED);
- }
- -(NSNumber*)isWritable:(id)_void
- {
- return NUMBOOL(internalState & SOCKET_CONNECTED);
- }
- -(int)readToBuffer:(TiBuffer*)buffer offset:(int)offset length:(int)length callback:(KrollCallback *)callback
- {
- // TODO: Put this in the write()/read() wrappers when they're being called consistently, blah blah blah
- if ([[buffer data] length] == 0 && length != 0) {
- if (callback != nil) {
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"source",NUMINT(0),@"bytesProcessed", nil];
- [self _fireEventToListener:@"read" withObject:event listener:callback thisObject:nil];
- }
- return 0;
- }
- // As always, ensure that operations take place on the socket thread...
- if ([NSThread currentThread] != socketThread) {
- NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(readToBuffer:offset:length:callback:)]];
- [invocation setTarget:self];
- [invocation setSelector:@selector(readToBuffer:offset:length:callback:)];
- [invocation setArgument:&buffer atIndex:2];
- [invocation setArgument:&offset atIndex:3];
- [invocation setArgument:&length atIndex:4];
- [invocation setArgument:&callback atIndex:5];
- [invocation retainArguments];
-
- if (callback == nil) {
- SAFE_WAIT(ioCondition, invocation);
- if (socketError != nil) {
- // Have to autorelease because we need to hold onto it for throwing the exception
- RELEASE_TO_NIL_AUTORELEASE(socketError);
- [self throwException:@"TiSocketError"
- subreason:[socketError localizedDescription]
- location:CODELOCATION];
- }
- }
- else { // Queue up that invocation and go home
- [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
- }
-
- return readDataLength;
- }
- else {
- int tag = -1;
- if (callback != nil) {
- tag = asynchTagCount;
- NSDictionary* asynchInfo = [NSDictionary dictionaryWithObjectsAndKeys:buffer,@"buffer",callback,@"callback",NUMINT(TO_BUFFER),@"type",NUMINT(0),@"errorState",@"",@"errorDescription",nil];
- [operationInfo setObject:asynchInfo forKey:NUMINT(tag)];
- asynchTagCount = (asynchTagCount + 1) % INT_MAX;
- }
-
- [socket readDataWithTimeout:-1
- buffer:[buffer data]
- bufferOffset:offset
- maxLength:length
- tag:tag];
- }
-
- return 0; // Bogus return value; the real value is returned when we finish the read
- }
- -(int)writeFromBuffer:(TiBuffer*)buffer offset:(int)offset length:(int)length callback:(KrollCallback *)callback
- {
- // TODO: Put this in the write()/read() wrappers when they're being called consistently, blah blah blah
- if ([[buffer data] length] == 0) {
- if (callback != nil) {
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"source",NUMINT(0),@"bytesProcessed",NUMINT(0),@"errorState",@"",@"errorDescription", nil];
- [self _fireEventToListener:@"write" withObject:event listener:callback thisObject:nil];
- }
- return 0;
- }
-
- // As always, ensure that operations take place on the socket thread...
- if ([NSThread currentThread] != socketThread) {
- NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(writeFromBuffer:offset:length:callback:)]];
- [invocation setTarget:self];
- [invocation setSelector:@selector(writeFromBuffer:offset:length:callback:)];
- [invocation setArgument:&buffer atIndex:2];
- [invocation setArgument:&offset atIndex:3];
- [invocation setArgument:&length atIndex:4];
- [invocation setArgument:&callback atIndex:5];
- [invocation retainArguments];
-
- if (callback == nil) {
- SAFE_WAIT(ioCondition, invocation);
- if (socketError != nil) {
- // Have to autorelease because we need to hold onto it for throwing the exception
- RELEASE_TO_NIL_AUTORELEASE(socketError);
- [self throwException:@"TiSocketError"
- subreason:[socketError localizedDescription]
- location:CODELOCATION];
- }
- }
- else {
- [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
- }
-
- int result = 0;
- [invocation getReturnValue:&result];
-
- return result;
- }
- else {
- NSData* subdata = [[buffer data] subdataWithRange:NSMakeRange(offset, length)];
- int tag = -1;
- if (callback != nil) {
- tag = asynchTagCount;
- NSDictionary* asynchInfo = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT([subdata length]),@"bytesProcessed",callback,@"callback", nil];
- [operationInfo setObject:asynchInfo forKey:NUMINT(tag)];
- asynchTagCount = (asynchTagCount + 1) % INT_MAX;
- }
- NSLog(@"Queuing up my write!");
- [socket writeData:subdata withTimeout:-1 tag:tag];
-
- // TODO: Actually need the amount of data written - similar to readDataLength, for writes
- return [subdata length];
- }
- }
- -(int)writeToStream:(id<TiStreamInternal>)output chunkSize:(int)size callback:(KrollCallback *)callback
- {
- if ([NSThread currentThread] != socketThread) {
- NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(writeToStream:chunkSize:callback:)]];
- [invocation setTarget:self];
- [invocation setSelector:@selector(writeToStream:chunkSize:callback:)];
- [invocation setArgument:&output atIndex:2];
- [invocation setArgument:&size atIndex:3];
- [invocation setArgument:&callback atIndex:4];
- [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
- [invocation retainArguments];
-
- if (callback == nil) {
- SAFE_WAIT(ioCondition, invocation);
- if (socketError != nil) {
- // Have to autorelease because we need to hold onto it for throwing the exception
- RELEASE_TO_NIL_AUTORELEASE(socketError);
- [self throwException:@"TiSocketError"
- subreason:[socketError localizedDescription]
- location:CODELOCATION];
- }
- }
- else {
- [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
- }
-
- return readDataLength;
- }
- else {
- int tag = asynchTagCount;
- NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:output,@"destination",NUMINT(size),@"chunkSize",callback,@"callback",NUMINT(TO_STREAM),@"type", nil];
- [operationInfo setObject:info forKey:NUMINT(tag)];
- asynchTagCount = (asynchTagCount + 1) % INT_MAX;
-
- [socket readDataWithTimeout:-1
- buffer:nil
- bufferOffset:0
- maxLength:size
- tag:tag];
-
- return readDataLength;
- }
- }
- -(void)pumpToCallback:(KrollCallback *)callback chunkSize:(int)size asynch:(BOOL)asynch
- {
- if ([NSThread currentThread] != socketThread) {
- NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(pumpToCallback:chunkSize:asynch:)]];
- [invocation setTarget:self];
- [invocation setSelector:@selector(pumpToCallback:chunkSize:asynch:)];
- [invocation setArgument:&callback atIndex:2];
- [invocation setArgument:&size atIndex:3];
- [invocation setArgument:&asynch atIndex:4];
- [invocation retainArguments];
-
- if (!asynch) {
- SAFE_WAIT(ioCondition, invocation);
- if (socketError != nil) {
- // Have to autorelease because we need to hold onto it for throwing the exception
- RELEASE_TO_NIL_AUTORELEASE(socketError);
- [self throwException:@"TiSocketError"
- subreason:[socketError localizedDescription]
- location:CODELOCATION];
- }
- }
- else {
- [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
- }
-
- }
- else {
- int tag = asynchTagCount;
- NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(size),@"chunkSize",callback,@"callback",NUMINT(TO_CALLBACK),@"type", nil];
- [operationInfo setObject:info forKey:NUMINT(tag)];
- asynchTagCount = (asynchTagCount + 1) % INT_MAX;
-
- [socket readDataWithTimeout:-1
- buffer:nil
- bufferOffset:0
- maxLength:size
- tag:tag];
- }
- }
- #pragma mark AsyncSocketDelegate methods
- -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
- {
- // This gets called for sockets created via accepting, so return if the connected socket is NOT us
- if (sock != socket) {
- return;
- }
-
- internalState = SOCKET_CONNECTED;
-
- if (connected != nil) {
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket",nil];
- [self _fireEventToListener:@"connected" withObject:event listener:connected thisObject:self];
- }
- }
- // Prevent that goofy race conditon where a socket isn't attached to a run loop before beginning the accepted socket run loop.
- -(void)onSocketReadyInRunLoop:(AsyncSocket *)sock
- {
- NSCondition* tempConditionRef = [readyCondition retain];
- [tempConditionRef lock];
- socketReady = YES;
- [tempConditionRef signal];
- [tempConditionRef unlock];
- [tempConditionRef release];
- }
- -(void)onSocketDidDisconnect:(AsyncSocket *)sock
- {
- // Triggered when we error out, so don't fire in that situation
- if (!(internalState & SOCKET_ERROR)) {
- // May also be triggered when we're already "closed"
- if (!(internalState & SOCKET_CLOSED)) {
- internalState = SOCKET_CLOSED;
- if (closed != nil) {
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket", nil];
- [self _fireEventToListener:@"closed" withObject:event listener:closed thisObject:self];
- }
- }
- }
-
- // Signal any waiting I/O
- // TODO: Be sure to handle any signal on closing
- [ioCondition lock];
- [ioCondition broadcast];
- [ioCondition unlock];
- }
- // Called 1st in the accept process
- - (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
- {
- [acceptArgs setValue:newSocket forKey:SOCK_KEY];
- [self performSelectorInBackground:@selector(startAcceptedSocket:) withObject:acceptArgs];
- accepting = NO;
- }
-
- // Called 2nd in the accept process
- -(NSRunLoop*)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket
- {
- // We start up the accepted socket thread, and wait for the run loop to be cached, and return it...
- NSCondition* tempConditionRef = [readyCondition retain];
- [tempConditionRef lock];
- if (acceptRunLoop == nil) {
- [tempConditionRef wait];
- }
- NSRunLoop* currentRunLoop = acceptRunLoop;
- acceptRunLoop = nil;
- [tempConditionRef unlock];
- [tempConditionRef release];
-
- return currentRunLoop;
- }
- // TODO: As per AsyncSocket docs, may want to call "unreadData" and return that information, or at least allow access to it via some other method
- -(BOOL)onSocket:(AsyncSocket *)sock shouldDisconnectWithError:(NSError *)err
- {
- if (err != nil) {
- internalState = SOCKET_ERROR;
-
- // TODO: We need the information about # bytes written, # bytes read before firing these...
- // That means inside asynchSocket, we're going to have to start tracking the read/write ops manually. More mods.
- for (NSDictionary* info in [operationInfo objectEnumerator]) {
- KrollCallback* callback = [info valueForKey:@"callback"];
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT([err code]),@"errorState",[err localizedDescription],@"errorDescription", nil];
- [self _fireEventToListener:@"error" withObject:event listener:callback thisObject:nil];
- }
-
- if (error != nil) {
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket",NUMINT([err code]),@"errorCode",[err localizedDescription],@"error",nil];
- [self _fireEventToListener:@"error" withObject:event listener:error thisObject:self];
- }
-
- socketError = [err retain]; // Used for synchronous I/O error handling
- return YES;
- }
- else { // Remote hangup; we encountered EOF, and should not close (but should return -1 to asynch info, and as the # bytes read/written)
- // Trigger callbacks
- for (NSDictionary* info in [operationInfo objectEnumerator]) {
- KrollCallback* callback = [info valueForKey:@"callback"];
- ReadDestination type = [[info objectForKey:@"type"] intValue];
- NSDictionary* event = nil;
- NSString* name = nil;
-
- switch (type) {
- case TO_BUFFER: {
- name = @"read";
-
- event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(0),@"errorState",@"",@"errorDescription",NUMINT(-1),@"bytesProcessed",self,@"source", nil];
- break;
- }
- case TO_STREAM: {
- name = @"writeStream";
- id<TiStreamInternal> stream = [info valueForKey:@"destination"];
-
- event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(0),@"errorState",@"",@"errorDescription",NUMINT(-1),@"bytesProcessed",self,@"fromStream",stream,@"toStream", nil];
- }
- case TO_CALLBACK: {
- name = @"pump";
-
- event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(0),@"errorState",@"",@"errorDescription",NUMINT(-1),@"bytesProcessed",self,@"source",NUMINT(readDataLength),@"totalBytesProcessed",[NSNull null],@"buffer",nil];
- }
- default: {
- name = @"write";
- event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(0),@"errorState",@"",@"errorDescription",NUMINT(-1),@"bytesProcessed",self,@"source", nil];
- break;
- }
- }
- [self _fireEventToListener:name withObject:event listener:callback thisObject:nil];
- }
- readDataLength = -1;
-
- // Unlock any synch I/O
- [ioCondition lock];
- [ioCondition broadcast];
- [ioCondition unlock];
-
- return NO;
- }
- }
- -(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
- {
- // Result of asynch write
- if (tag > -1) {
- NSDictionary* info = [operationInfo objectForKey:NUMINT(tag)];
- KrollCallback* callback = [info valueForKey:@"callback"];
-
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:[info valueForKey:@"bytesProcessed"],@"bytesProcessed",NUMINT(0),@"errorState",@"",@"errorDescription",nil];
- [self _fireEventToListener:@"write" withObject:event listener:callback thisObject:self];
- [operationInfo removeObjectForKey:NUMINT(tag)];
- }
- else {
- // Signal the IO condition
- [ioCondition lock];
- [ioCondition signal];
- [ioCondition unlock];
- }
- }
- // 'Read' can also lead to a writeStream/pump operation
- -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
- {
- // We do NOT SIGNAL I/O if dealing with a tagged operation. The reason why? Because toStream/pump need to keep streaming and pumping...
- // until the socket is closed, which fires the I/O condition signal.
-
- // Specialized operation
- if (tag > -1) {
- NSDictionary* info = [operationInfo objectForKey:NUMINT(tag)];
- ReadDestination type = [[info objectForKey:@"type"] intValue];
- switch (type) {
- case TO_BUFFER: {
- KrollCallback* callback = [info valueForKey:@"callback"];
- TiBuffer* buffer = [info valueForKey:@"buffer"];
-
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:buffer,@"buffer",NUMINT([data length]),@"bytesProcessed",NUMINT(0),@"errorState",@"",@"errorDescription", nil];
- [self _fireEventToListener:@"read" withObject:event listener:callback thisObject:self];
- break;
- }
- case TO_STREAM: {
- // Perform the write to stream
- id<TiStreamInternal> stream = [info valueForKey:@"destination"];
- int size = [TiUtils intValue:[info valueForKey:@"chunkSize"]];
- KrollCallback* callback = [info valueForKey:@"callback"];
-
- TiBuffer* tempBuffer = [[[TiBuffer alloc] _initWithPageContext:[self executionContext]] autorelease];
- [tempBuffer setData:[NSMutableData dataWithData:data]];
- readDataLength += [data length];
-
- // TODO: We need to be able to monitor this stream for write errors, and then report back via an exception or the callback or whatever
- [stream writeFromBuffer:tempBuffer offset:0 length:[data length] callback:nil];
-
- // ... And then set up the next read to it.
- [self writeToStream:stream chunkSize:size callback:callback];
- break;
- }
- case TO_CALLBACK: {
- // Perform the pump to callback
- KrollCallback* callback = [info valueForKey:@"callback"];
- int size = [TiUtils intValue:[info valueForKey:@"chunkSize"]];
-
- TiBuffer* tempBuffer = [[[TiBuffer alloc] _initWithPageContext:[self executionContext]] autorelease];
- [tempBuffer setData:[NSMutableData dataWithData:data]];
- readDataLength += [data length];
-
- NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"source",tempBuffer,@"buffer",NUMINT([data length]),@"bytesProcessed",NUMINT(readDataLength),@"totalBytesProcessed", NUMINT(0),@"errorState",@"",@"errorDescription",nil];
- [self _fireEventToListener:@"pump" withObject:event listener:callback thisObject:nil];
-
- // ... And queue up the next pump.
- [self pumpToCallback:callback chunkSize:size asynch:YES]; // Only consider the 1st pump "synchronous", that's the one which blocks!
- break;
- }
- }
- [operationInfo removeObjectForKey:NUMINT(tag)];
- }
- else {
- // Only signal the condition for your standard blocking read
- // The amount of data read is available only off of this 'data' object... not off the initial buffer we passed.
- [ioCondition lock];
- readDataLength = [data length];
- [ioCondition signal];
- [ioCondition unlock];
- }
- }
- @end
- #endif