PageRenderTime 47ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/ep-020/test_longpress_pinch/build/iphone/Classes/TiNetworkSocketTCPProxy.m

https://gitlab.com/intelij/Forging-Titanium
Objective C | 834 lines | 634 code | 131 blank | 69 comment | 105 complexity | 1e191b251ca6fec38f310f9cc749e21f MD5 | raw file
Possible License(s): Apache-2.0
  1. /**
  2. * Appcelerator Titanium Mobile
  3. * Copyright (c) 2009-2011 by test_longpress_pinch, Inc. All Rights Reserved.
  4. * Licensed under the terms of the Apache Public License
  5. * Please see the LICENSE included with this distribution for details.
  6. *
  7. * WARNING: This is generated code. Modify at your own risk and without support.
  8. */
  9. #ifdef USE_TI_NETWORKSOCKET
  10. #import "TiNetworkSocketTCPProxy.h"
  11. #import "NetworkModule.h"
  12. #import "TiBlob.h"
  13. #import "TiBuffer.h"
  14. static NSString* SOCK_KEY = @"socket";
  15. static NSString* ARG_KEY = @"arg";
  16. @interface TiNetworkSocketTCPProxy (Private)
  17. -(void)cleanupSocket;
  18. -(void)setConnectedSocket:(AsyncSocket*)sock;
  19. -(void)startConnectingSocket;
  20. -(void)startListeningSocket;
  21. -(void)startAcceptedSocket:(NSDictionary*)info;
  22. -(void)socketRunLoop;
  23. @end
  24. @implementation TiNetworkSocketTCPProxy
  25. @synthesize host, connected, accepted, closed, error;
  26. #pragma mark Internals
  27. -(id)init
  28. {
  29. if (self = [super init]) {
  30. listening = [[NSCondition alloc] init];
  31. ioCondition = [[NSCondition alloc] init];
  32. internalState = SOCKET_INITIALIZED;
  33. socketThread = nil;
  34. acceptRunLoop = nil;
  35. socketReady = NO;
  36. acceptArgs = [[NSMutableDictionary alloc] init];
  37. readyCondition = [[NSCondition alloc] init];
  38. operationInfo = [[NSMutableDictionary alloc] init];
  39. asynchTagCount = 0;
  40. }
  41. return self;
  42. }
  43. -(void)dealloc
  44. {
  45. // Calls _destroy
  46. [super dealloc];
  47. }
  48. -(void)_destroy
  49. {
  50. internalState = SOCKET_CLOSED;
  51. // Socket cleanup, if necessary
  52. if ([socketThread isExecuting]) {
  53. [self performSelector:@selector(cleanupSocket) onThread:socketThread withObject:nil waitUntilDone:YES];
  54. }
  55. RELEASE_TO_NIL(operationInfo);
  56. RELEASE_TO_NIL(acceptArgs);
  57. RELEASE_TO_NIL(socketError);
  58. RELEASE_TO_NIL(connected);
  59. RELEASE_TO_NIL(accepted);
  60. RELEASE_TO_NIL(closed);
  61. RELEASE_TO_NIL(error);
  62. // Release the conditions... so long as each condition has been retained, this is safe.
  63. RELEASE_TO_NIL(listening);
  64. RELEASE_TO_NIL(ioCondition);
  65. RELEASE_TO_NIL(readyCondition);
  66. RELEASE_TO_NIL(host);
  67. [super _destroy];
  68. }
  69. -(void)cleanupSocket
  70. {
  71. // Signal anything that's still waiting on the socket, just to be safe
  72. [listening lock];
  73. [listening broadcast];
  74. [listening unlock];
  75. [ioCondition lock];
  76. [ioCondition broadcast];
  77. [ioCondition unlock];
  78. [readyCondition lock];
  79. [readyCondition broadcast];
  80. [readyCondition unlock];
  81. [socket disconnect];
  82. [socket setDelegate:nil];
  83. RELEASE_TO_NIL(socket);
  84. }
  85. -(void)setConnectedSocket:(AsyncSocket*)sock
  86. {
  87. [self cleanupSocket];
  88. internalState = SOCKET_CONNECTED;
  89. socket = [sock retain];
  90. [socket setDelegate:self];
  91. socketThread = [NSThread currentThread];
  92. }
  93. #define AUTORELEASE_LOOP 5
  94. -(void)socketRunLoop
  95. {
  96. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  97. [socketThread setName:[NSString stringWithFormat:@"Ti.Network.Socket.TCP (%x)",self]];
  98. // Begin the run loop for the socket
  99. int counter=0;
  100. while (!(internalState & (SOCKET_CLOSED | SOCKET_ERROR)) &&
  101. [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]])
  102. {
  103. if (++counter == AUTORELEASE_LOOP) {
  104. [pool release];
  105. pool = [[NSAutoreleasePool alloc] init];
  106. counter = 0;
  107. }
  108. }
  109. // No longer send messages to this thread!
  110. socketThread = nil;
  111. [pool release];
  112. }
  113. -(void)startConnectingSocket;
  114. {
  115. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  116. id port = [self valueForUndefinedKey:@"port"]; // Can be string or int
  117. NSNumber* timeout = [self valueForUndefinedKey:@"timeout"];
  118. // TODO: We're on a BG thread... need to make sure this gets caught somehow.
  119. socket = [[AsyncSocket alloc] initWithDelegate:self];
  120. socketThread = [NSThread currentThread];
  121. NSError* err = nil;
  122. BOOL success;
  123. if (timeout == nil) {
  124. success = [socket connectToHost:host onPort:[TiUtils intValue:port] error:&err];
  125. }
  126. else {
  127. success = [socket connectToHost:host onPort:[port intValue] withTimeout:[timeout doubleValue] error:&err];
  128. }
  129. if (err || !success) {
  130. internalState = SOCKET_ERROR;
  131. [self cleanupSocket];
  132. if (error != nil) {
  133. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket",[err localizedDescription],@"error",NUMINT([err code]),@"errorCode", nil];
  134. [self _fireEventToListener:@"error" withObject:event listener:error thisObject:self];
  135. }
  136. socketThread = nil;
  137. [pool release];
  138. return;
  139. }
  140. [self socketRunLoop];
  141. [pool release];
  142. }
  143. -(void)startListeningSocket;
  144. {
  145. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  146. id port = [self valueForKey:@"port"]; // Can be string or int
  147. socket = [[AsyncSocket alloc] initWithDelegate:self];
  148. NSError* err = nil;
  149. BOOL success = [socket acceptOnInterface:host port:[port intValue] autoaccept:NO error:&err];
  150. if (err || !success) {
  151. internalState = SOCKET_ERROR;
  152. socketError = [err retain];
  153. [self cleanupSocket]; // Cleanup signals the condition
  154. [pool release];
  155. return;
  156. }
  157. // Make sure that we 'accept' only when explicitly requested
  158. CFSocketRef cfSocket = [socket getCFSocket];
  159. CFOptionFlags options = CFSocketGetSocketFlags(cfSocket);
  160. CFSocketSetSocketFlags(cfSocket, options & ~kCFSocketAutomaticallyReenableAcceptCallBack);
  161. socketThread = [NSThread currentThread];
  162. [listening lock];
  163. internalState = SOCKET_LISTENING;
  164. [listening signal];
  165. [listening unlock];
  166. [self socketRunLoop];
  167. [pool release];
  168. }
  169. -(void)startAcceptedSocket:(NSDictionary*)info
  170. {
  171. NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
  172. // 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)
  173. // 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
  174. // overhead, but it probably prevents DUMB CRASHES from happening.
  175. // 1. Provide the run loop to the new socket...
  176. NSCondition* tempConditionRef = [readyCondition retain];
  177. [tempConditionRef lock];
  178. acceptRunLoop = [NSRunLoop currentRunLoop];
  179. [tempConditionRef signal];
  180. [tempConditionRef unlock];
  181. [tempConditionRef release];
  182. // 2. ... And then wait for it to attach to the run loop.
  183. AsyncSocket* newSocket = [[[info objectForKey:SOCK_KEY] retain] autorelease];
  184. NSArray* arg = [info objectForKey:ARG_KEY];
  185. TiNetworkSocketTCPProxy* proxy = [[[TiNetworkSocketTCPProxy alloc] _initWithPageContext:[self executionContext] args:arg] autorelease];
  186. [proxy rememberSelf];
  187. [proxy setConnectedSocket:newSocket];
  188. // TODO: remoteHost/remotePort & host/port?
  189. [proxy setValue:[newSocket connectedHost] forKey:@"host"];
  190. [proxy setValue:NUMINT([newSocket connectedPort]) forKey:@"port"];
  191. if (accepted != nil) {
  192. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket",proxy,@"inbound",nil];
  193. [self _fireEventToListener:@"accepted" withObject:event listener:accepted thisObject:self];
  194. }
  195. tempConditionRef = [readyCondition retain];
  196. [tempConditionRef lock];
  197. if (!socketReady) {
  198. [tempConditionRef wait];
  199. }
  200. [tempConditionRef unlock];
  201. [tempConditionRef release];
  202. [proxy socketRunLoop];
  203. [proxy forgetSelf];
  204. [pool release];
  205. }
  206. #pragma mark Public API : Functions
  207. // Used to bump API calls onto the socket thread if necessary
  208. #define ENSURE_SOCKET_THREAD(f,x) \
  209. if (socketThread == nil) { \
  210. return; \
  211. } \
  212. if ([NSThread currentThread] != socketThread) { \
  213. [self performSelector:@selector(f:) onThread:socketThread withObject:x waitUntilDone:YES]; \
  214. return; \
  215. } \
  216. // Convenience for io waits
  217. #define SAFE_WAIT(condition, invocation) \
  218. {\
  219. NSCondition* temp = [condition retain]; \
  220. [temp lock]; \
  221. [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO]; \
  222. [temp wait]; \
  223. [temp unlock]; \
  224. [temp release]; \
  225. }\
  226. -(void)connect:(id)_void
  227. {
  228. if (!(internalState & SOCKET_INITIALIZED)) {
  229. [self throwException:[NSString stringWithFormat:@"Attempt to connect with bad state: %d",internalState]
  230. subreason:nil
  231. location:CODELOCATION];
  232. return;
  233. }
  234. if (host == nil || [host isEqual:@""]) {
  235. // Use the loopback
  236. [self setHost:@"127.0.0.1"];
  237. }
  238. [self performSelectorInBackground:@selector(startConnectingSocket) withObject:nil];
  239. }
  240. -(void)listen:(id)arg
  241. {
  242. if (!(internalState & SOCKET_INITIALIZED)) {
  243. [self throwException:[NSString stringWithFormat:@"Attempt to listen with bad state: %d", internalState]
  244. subreason:nil
  245. location:CODELOCATION];
  246. return;
  247. }
  248. if (host == nil || [host isEqual:@""]) {
  249. // Use the loopback
  250. [self setHost:@"127.0.0.1"];
  251. }
  252. [self performSelectorInBackground:@selector(startListeningSocket) withObject:nil];
  253. // Call should block until we're listening or have an error
  254. NSCondition* tempConditionRef = [listening retain];
  255. [tempConditionRef lock];
  256. if (!(internalState & (SOCKET_LISTENING | SOCKET_ERROR))) {
  257. [tempConditionRef wait];
  258. }
  259. [tempConditionRef unlock];
  260. [tempConditionRef release];
  261. if (internalState & SOCKET_ERROR) {
  262. if (socketError != nil) {
  263. // Have to autorelease because we need to hold onto it for throwing the exception
  264. RELEASE_TO_NIL_AUTORELEASE(socketError);
  265. [self throwException:@"TiSocketError"
  266. subreason:[socketError localizedDescription]
  267. location:CODELOCATION];
  268. }
  269. }
  270. }
  271. -(void)accept:(id)arg
  272. {
  273. // Only change the accept args if we have an accept in progress
  274. // TODO: Probably want a lock for this...
  275. if (accepting) {
  276. [acceptArgs setValue:arg forKey:ARG_KEY];
  277. return;
  278. }
  279. ENSURE_SOCKET_THREAD(accept,arg);
  280. NSDictionary* args = nil;
  281. ENSURE_ARG_OR_NIL_AT_INDEX(args, arg, 0, NSDictionary);
  282. [acceptArgs setValue:arg forKey:ARG_KEY];
  283. CFSocketRef sock = [socket getCFSocket];
  284. CFSocketEnableCallBacks(sock, kCFSocketAcceptCallBack);
  285. accepting = YES;
  286. }
  287. // TODO: Bump into 'closed' state if socketThread==nil
  288. -(void)close:(id)_void
  289. {
  290. // Don't switch threads until after the state check; this allows us to throw the exception on the right thread
  291. if (!(internalState & (SOCKET_CONNECTED | SOCKET_LISTENING | SOCKET_INITIALIZED))) {
  292. [self throwException:[NSString stringWithFormat:@"Attempt to close in invalid state: %d",internalState]
  293. subreason:nil
  294. location:CODELOCATION];
  295. return;
  296. }
  297. ENSURE_SOCKET_THREAD(close,_void);
  298. [self cleanupSocket];
  299. internalState = SOCKET_CLOSED;
  300. }
  301. #pragma mark Public API : Properties
  302. -(NSNumber*)state
  303. {
  304. return NUMINT(internalState);
  305. }
  306. // TODO: Move to TiBase?
  307. #define TYPESAFE_SETTER(funcname,prop,type) \
  308. -(void)funcname:(type*)val \
  309. { \
  310. ENSURE_TYPE_OR_NIL(val,type); \
  311. if (prop != val) { \
  312. [prop release]; \
  313. prop = [val retain]; \
  314. }\
  315. }
  316. TYPESAFE_SETTER(setHost, host, NSString)
  317. TYPESAFE_SETTER(setConnected, connected, KrollCallback)
  318. TYPESAFE_SETTER(setAccepted, accepted, KrollCallback)
  319. TYPESAFE_SETTER(setClosed, closed, KrollCallback)
  320. TYPESAFE_SETTER(setError, error, KrollCallback)
  321. #pragma mark TiStreamInternal implementations
  322. -(NSNumber*)isReadable:(id)_void
  323. {
  324. return NUMBOOL(internalState & SOCKET_CONNECTED);
  325. }
  326. -(NSNumber*)isWritable:(id)_void
  327. {
  328. return NUMBOOL(internalState & SOCKET_CONNECTED);
  329. }
  330. -(int)readToBuffer:(TiBuffer*)buffer offset:(int)offset length:(int)length callback:(KrollCallback *)callback
  331. {
  332. // TODO: Put this in the write()/read() wrappers when they're being called consistently, blah blah blah
  333. if ([[buffer data] length] == 0 && length != 0) {
  334. if (callback != nil) {
  335. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"source",NUMINT(0),@"bytesProcessed", nil];
  336. [self _fireEventToListener:@"read" withObject:event listener:callback thisObject:nil];
  337. }
  338. return 0;
  339. }
  340. // As always, ensure that operations take place on the socket thread...
  341. if ([NSThread currentThread] != socketThread) {
  342. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(readToBuffer:offset:length:callback:)]];
  343. [invocation setTarget:self];
  344. [invocation setSelector:@selector(readToBuffer:offset:length:callback:)];
  345. [invocation setArgument:&buffer atIndex:2];
  346. [invocation setArgument:&offset atIndex:3];
  347. [invocation setArgument:&length atIndex:4];
  348. [invocation setArgument:&callback atIndex:5];
  349. [invocation retainArguments];
  350. if (callback == nil) {
  351. SAFE_WAIT(ioCondition, invocation);
  352. if (socketError != nil) {
  353. // Have to autorelease because we need to hold onto it for throwing the exception
  354. RELEASE_TO_NIL_AUTORELEASE(socketError);
  355. [self throwException:@"TiSocketError"
  356. subreason:[socketError localizedDescription]
  357. location:CODELOCATION];
  358. }
  359. }
  360. else { // Queue up that invocation and go home
  361. [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
  362. }
  363. return readDataLength;
  364. }
  365. else {
  366. int tag = -1;
  367. if (callback != nil) {
  368. tag = asynchTagCount;
  369. NSDictionary* asynchInfo = [NSDictionary dictionaryWithObjectsAndKeys:buffer,@"buffer",callback,@"callback",NUMINT(TO_BUFFER),@"type",NUMINT(0),@"errorState",@"",@"errorDescription",nil];
  370. [operationInfo setObject:asynchInfo forKey:NUMINT(tag)];
  371. asynchTagCount = (asynchTagCount + 1) % INT_MAX;
  372. }
  373. [socket readDataWithTimeout:-1
  374. buffer:[buffer data]
  375. bufferOffset:offset
  376. maxLength:length
  377. tag:tag];
  378. }
  379. return 0; // Bogus return value; the real value is returned when we finish the read
  380. }
  381. -(int)writeFromBuffer:(TiBuffer*)buffer offset:(int)offset length:(int)length callback:(KrollCallback *)callback
  382. {
  383. // TODO: Put this in the write()/read() wrappers when they're being called consistently, blah blah blah
  384. if ([[buffer data] length] == 0) {
  385. if (callback != nil) {
  386. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"source",NUMINT(0),@"bytesProcessed",NUMINT(0),@"errorState",@"",@"errorDescription", nil];
  387. [self _fireEventToListener:@"write" withObject:event listener:callback thisObject:nil];
  388. }
  389. return 0;
  390. }
  391. // As always, ensure that operations take place on the socket thread...
  392. if ([NSThread currentThread] != socketThread) {
  393. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(writeFromBuffer:offset:length:callback:)]];
  394. [invocation setTarget:self];
  395. [invocation setSelector:@selector(writeFromBuffer:offset:length:callback:)];
  396. [invocation setArgument:&buffer atIndex:2];
  397. [invocation setArgument:&offset atIndex:3];
  398. [invocation setArgument:&length atIndex:4];
  399. [invocation setArgument:&callback atIndex:5];
  400. [invocation retainArguments];
  401. if (callback == nil) {
  402. SAFE_WAIT(ioCondition, invocation);
  403. if (socketError != nil) {
  404. // Have to autorelease because we need to hold onto it for throwing the exception
  405. RELEASE_TO_NIL_AUTORELEASE(socketError);
  406. [self throwException:@"TiSocketError"
  407. subreason:[socketError localizedDescription]
  408. location:CODELOCATION];
  409. }
  410. }
  411. else {
  412. [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
  413. }
  414. int result = 0;
  415. [invocation getReturnValue:&result];
  416. return result;
  417. }
  418. else {
  419. NSData* subdata = [[buffer data] subdataWithRange:NSMakeRange(offset, length)];
  420. int tag = -1;
  421. if (callback != nil) {
  422. tag = asynchTagCount;
  423. NSDictionary* asynchInfo = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT([subdata length]),@"bytesProcessed",callback,@"callback", nil];
  424. [operationInfo setObject:asynchInfo forKey:NUMINT(tag)];
  425. asynchTagCount = (asynchTagCount + 1) % INT_MAX;
  426. }
  427. NSLog(@"Queuing up my write!");
  428. [socket writeData:subdata withTimeout:-1 tag:tag];
  429. // TODO: Actually need the amount of data written - similar to readDataLength, for writes
  430. return [subdata length];
  431. }
  432. }
  433. -(int)writeToStream:(id<TiStreamInternal>)output chunkSize:(int)size callback:(KrollCallback *)callback
  434. {
  435. if ([NSThread currentThread] != socketThread) {
  436. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(writeToStream:chunkSize:callback:)]];
  437. [invocation setTarget:self];
  438. [invocation setSelector:@selector(writeToStream:chunkSize:callback:)];
  439. [invocation setArgument:&output atIndex:2];
  440. [invocation setArgument:&size atIndex:3];
  441. [invocation setArgument:&callback atIndex:4];
  442. [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
  443. [invocation retainArguments];
  444. if (callback == nil) {
  445. SAFE_WAIT(ioCondition, invocation);
  446. if (socketError != nil) {
  447. // Have to autorelease because we need to hold onto it for throwing the exception
  448. RELEASE_TO_NIL_AUTORELEASE(socketError);
  449. [self throwException:@"TiSocketError"
  450. subreason:[socketError localizedDescription]
  451. location:CODELOCATION];
  452. }
  453. }
  454. else {
  455. [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
  456. }
  457. return readDataLength;
  458. }
  459. else {
  460. int tag = asynchTagCount;
  461. NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:output,@"destination",NUMINT(size),@"chunkSize",callback,@"callback",NUMINT(TO_STREAM),@"type", nil];
  462. [operationInfo setObject:info forKey:NUMINT(tag)];
  463. asynchTagCount = (asynchTagCount + 1) % INT_MAX;
  464. [socket readDataWithTimeout:-1
  465. buffer:nil
  466. bufferOffset:0
  467. maxLength:size
  468. tag:tag];
  469. return readDataLength;
  470. }
  471. }
  472. -(void)pumpToCallback:(KrollCallback *)callback chunkSize:(int)size asynch:(BOOL)asynch
  473. {
  474. if ([NSThread currentThread] != socketThread) {
  475. NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:@selector(pumpToCallback:chunkSize:asynch:)]];
  476. [invocation setTarget:self];
  477. [invocation setSelector:@selector(pumpToCallback:chunkSize:asynch:)];
  478. [invocation setArgument:&callback atIndex:2];
  479. [invocation setArgument:&size atIndex:3];
  480. [invocation setArgument:&asynch atIndex:4];
  481. [invocation retainArguments];
  482. if (!asynch) {
  483. SAFE_WAIT(ioCondition, invocation);
  484. if (socketError != nil) {
  485. // Have to autorelease because we need to hold onto it for throwing the exception
  486. RELEASE_TO_NIL_AUTORELEASE(socketError);
  487. [self throwException:@"TiSocketError"
  488. subreason:[socketError localizedDescription]
  489. location:CODELOCATION];
  490. }
  491. }
  492. else {
  493. [invocation performSelector:@selector(invoke) onThread:socketThread withObject:nil waitUntilDone:NO];
  494. }
  495. }
  496. else {
  497. int tag = asynchTagCount;
  498. NSDictionary* info = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(size),@"chunkSize",callback,@"callback",NUMINT(TO_CALLBACK),@"type", nil];
  499. [operationInfo setObject:info forKey:NUMINT(tag)];
  500. asynchTagCount = (asynchTagCount + 1) % INT_MAX;
  501. [socket readDataWithTimeout:-1
  502. buffer:nil
  503. bufferOffset:0
  504. maxLength:size
  505. tag:tag];
  506. }
  507. }
  508. #pragma mark AsyncSocketDelegate methods
  509. -(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
  510. {
  511. // This gets called for sockets created via accepting, so return if the connected socket is NOT us
  512. if (sock != socket) {
  513. return;
  514. }
  515. internalState = SOCKET_CONNECTED;
  516. if (connected != nil) {
  517. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket",nil];
  518. [self _fireEventToListener:@"connected" withObject:event listener:connected thisObject:self];
  519. }
  520. }
  521. // Prevent that goofy race conditon where a socket isn't attached to a run loop before beginning the accepted socket run loop.
  522. -(void)onSocketReadyInRunLoop:(AsyncSocket *)sock
  523. {
  524. NSCondition* tempConditionRef = [readyCondition retain];
  525. [tempConditionRef lock];
  526. socketReady = YES;
  527. [tempConditionRef signal];
  528. [tempConditionRef unlock];
  529. [tempConditionRef release];
  530. }
  531. -(void)onSocketDidDisconnect:(AsyncSocket *)sock
  532. {
  533. // Triggered when we error out, so don't fire in that situation
  534. if (!(internalState & SOCKET_ERROR)) {
  535. // May also be triggered when we're already "closed"
  536. if (!(internalState & SOCKET_CLOSED)) {
  537. internalState = SOCKET_CLOSED;
  538. if (closed != nil) {
  539. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket", nil];
  540. [self _fireEventToListener:@"closed" withObject:event listener:closed thisObject:self];
  541. }
  542. }
  543. }
  544. // Signal any waiting I/O
  545. // TODO: Be sure to handle any signal on closing
  546. [ioCondition lock];
  547. [ioCondition broadcast];
  548. [ioCondition unlock];
  549. }
  550. // Called 1st in the accept process
  551. - (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
  552. {
  553. [acceptArgs setValue:newSocket forKey:SOCK_KEY];
  554. [self performSelectorInBackground:@selector(startAcceptedSocket:) withObject:acceptArgs];
  555. accepting = NO;
  556. }
  557. // Called 2nd in the accept process
  558. -(NSRunLoop*)onSocket:(AsyncSocket *)sock wantsRunLoopForNewSocket:(AsyncSocket *)newSocket
  559. {
  560. // We start up the accepted socket thread, and wait for the run loop to be cached, and return it...
  561. NSCondition* tempConditionRef = [readyCondition retain];
  562. [tempConditionRef lock];
  563. if (acceptRunLoop == nil) {
  564. [tempConditionRef wait];
  565. }
  566. NSRunLoop* currentRunLoop = acceptRunLoop;
  567. acceptRunLoop = nil;
  568. [tempConditionRef unlock];
  569. [tempConditionRef release];
  570. return currentRunLoop;
  571. }
  572. // 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
  573. -(BOOL)onSocket:(AsyncSocket *)sock shouldDisconnectWithError:(NSError *)err
  574. {
  575. if (err != nil) {
  576. internalState = SOCKET_ERROR;
  577. // TODO: We need the information about # bytes written, # bytes read before firing these...
  578. // That means inside asynchSocket, we're going to have to start tracking the read/write ops manually. More mods.
  579. for (NSDictionary* info in [operationInfo objectEnumerator]) {
  580. KrollCallback* callback = [info valueForKey:@"callback"];
  581. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT([err code]),@"errorState",[err localizedDescription],@"errorDescription", nil];
  582. [self _fireEventToListener:@"error" withObject:event listener:callback thisObject:nil];
  583. }
  584. if (error != nil) {
  585. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"socket",NUMINT([err code]),@"errorCode",[err localizedDescription],@"error",nil];
  586. [self _fireEventToListener:@"error" withObject:event listener:error thisObject:self];
  587. }
  588. socketError = [err retain]; // Used for synchronous I/O error handling
  589. return YES;
  590. }
  591. else { // Remote hangup; we encountered EOF, and should not close (but should return -1 to asynch info, and as the # bytes read/written)
  592. // Trigger callbacks
  593. for (NSDictionary* info in [operationInfo objectEnumerator]) {
  594. KrollCallback* callback = [info valueForKey:@"callback"];
  595. ReadDestination type = [[info objectForKey:@"type"] intValue];
  596. NSDictionary* event = nil;
  597. NSString* name = nil;
  598. switch (type) {
  599. case TO_BUFFER: {
  600. name = @"read";
  601. event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(0),@"errorState",@"",@"errorDescription",NUMINT(-1),@"bytesProcessed",self,@"source", nil];
  602. break;
  603. }
  604. case TO_STREAM: {
  605. name = @"writeStream";
  606. id<TiStreamInternal> stream = [info valueForKey:@"destination"];
  607. event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(0),@"errorState",@"",@"errorDescription",NUMINT(-1),@"bytesProcessed",self,@"fromStream",stream,@"toStream", nil];
  608. }
  609. case TO_CALLBACK: {
  610. name = @"pump";
  611. event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(0),@"errorState",@"",@"errorDescription",NUMINT(-1),@"bytesProcessed",self,@"source",NUMINT(readDataLength),@"totalBytesProcessed",[NSNull null],@"buffer",nil];
  612. }
  613. default: {
  614. name = @"write";
  615. event = [NSDictionary dictionaryWithObjectsAndKeys:NUMINT(0),@"errorState",@"",@"errorDescription",NUMINT(-1),@"bytesProcessed",self,@"source", nil];
  616. break;
  617. }
  618. }
  619. [self _fireEventToListener:name withObject:event listener:callback thisObject:nil];
  620. }
  621. readDataLength = -1;
  622. // Unlock any synch I/O
  623. [ioCondition lock];
  624. [ioCondition broadcast];
  625. [ioCondition unlock];
  626. return NO;
  627. }
  628. }
  629. -(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
  630. {
  631. // Result of asynch write
  632. if (tag > -1) {
  633. NSDictionary* info = [operationInfo objectForKey:NUMINT(tag)];
  634. KrollCallback* callback = [info valueForKey:@"callback"];
  635. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:[info valueForKey:@"bytesProcessed"],@"bytesProcessed",NUMINT(0),@"errorState",@"",@"errorDescription",nil];
  636. [self _fireEventToListener:@"write" withObject:event listener:callback thisObject:self];
  637. [operationInfo removeObjectForKey:NUMINT(tag)];
  638. }
  639. else {
  640. // Signal the IO condition
  641. [ioCondition lock];
  642. [ioCondition signal];
  643. [ioCondition unlock];
  644. }
  645. }
  646. // 'Read' can also lead to a writeStream/pump operation
  647. -(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
  648. {
  649. // We do NOT SIGNAL I/O if dealing with a tagged operation. The reason why? Because toStream/pump need to keep streaming and pumping...
  650. // until the socket is closed, which fires the I/O condition signal.
  651. // Specialized operation
  652. if (tag > -1) {
  653. NSDictionary* info = [operationInfo objectForKey:NUMINT(tag)];
  654. ReadDestination type = [[info objectForKey:@"type"] intValue];
  655. switch (type) {
  656. case TO_BUFFER: {
  657. KrollCallback* callback = [info valueForKey:@"callback"];
  658. TiBuffer* buffer = [info valueForKey:@"buffer"];
  659. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:buffer,@"buffer",NUMINT([data length]),@"bytesProcessed",NUMINT(0),@"errorState",@"",@"errorDescription", nil];
  660. [self _fireEventToListener:@"read" withObject:event listener:callback thisObject:self];
  661. break;
  662. }
  663. case TO_STREAM: {
  664. // Perform the write to stream
  665. id<TiStreamInternal> stream = [info valueForKey:@"destination"];
  666. int size = [TiUtils intValue:[info valueForKey:@"chunkSize"]];
  667. KrollCallback* callback = [info valueForKey:@"callback"];
  668. TiBuffer* tempBuffer = [[[TiBuffer alloc] _initWithPageContext:[self executionContext]] autorelease];
  669. [tempBuffer setData:[NSMutableData dataWithData:data]];
  670. readDataLength += [data length];
  671. // 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
  672. [stream writeFromBuffer:tempBuffer offset:0 length:[data length] callback:nil];
  673. // ... And then set up the next read to it.
  674. [self writeToStream:stream chunkSize:size callback:callback];
  675. break;
  676. }
  677. case TO_CALLBACK: {
  678. // Perform the pump to callback
  679. KrollCallback* callback = [info valueForKey:@"callback"];
  680. int size = [TiUtils intValue:[info valueForKey:@"chunkSize"]];
  681. TiBuffer* tempBuffer = [[[TiBuffer alloc] _initWithPageContext:[self executionContext]] autorelease];
  682. [tempBuffer setData:[NSMutableData dataWithData:data]];
  683. readDataLength += [data length];
  684. NSDictionary* event = [NSDictionary dictionaryWithObjectsAndKeys:self,@"source",tempBuffer,@"buffer",NUMINT([data length]),@"bytesProcessed",NUMINT(readDataLength),@"totalBytesProcessed", NUMINT(0),@"errorState",@"",@"errorDescription",nil];
  685. [self _fireEventToListener:@"pump" withObject:event listener:callback thisObject:nil];
  686. // ... And queue up the next pump.
  687. [self pumpToCallback:callback chunkSize:size asynch:YES]; // Only consider the 1st pump "synchronous", that's the one which blocks!
  688. break;
  689. }
  690. }
  691. [operationInfo removeObjectForKey:NUMINT(tag)];
  692. }
  693. else {
  694. // Only signal the condition for your standard blocking read
  695. // The amount of data read is available only off of this 'data' object... not off the initial buffer we passed.
  696. [ioCondition lock];
  697. readDataLength = [data length];
  698. [ioCondition signal];
  699. [ioCondition unlock];
  700. }
  701. }
  702. @end
  703. #endif