PageRenderTime 57ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/io/libraries/CocoaHttpServer/WebSocket.m

https://gitlab.com/base.io/io
Objective C | 786 lines | 489 code | 149 blank | 148 comment | 82 complexity | f7abc21c146fc2f6f89d0ab0db0d3eea MD5 | raw file
  1. #import "WebSocket.h"
  2. #import "HTTPMessage.h"
  3. #import "GCDAsyncSocket.h"
  4. #import "DDNumber.h"
  5. #import "DDData.h"
  6. #import "HTTPLogging.h"
  7. #if ! __has_feature(objc_arc)
  8. #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
  9. #endif
  10. // Log levels: off, error, warn, info, verbose
  11. // Other flags : trace
  12. static const int httpLogLevel = HTTP_LOG_LEVEL_WARN; // | HTTP_LOG_FLAG_TRACE;
  13. #define TIMEOUT_NONE -1
  14. #define TIMEOUT_REQUEST_BODY 10
  15. #define TAG_HTTP_REQUEST_BODY 100
  16. #define TAG_HTTP_RESPONSE_HEADERS 200
  17. #define TAG_HTTP_RESPONSE_BODY 201
  18. #define TAG_PREFIX 300
  19. #define TAG_MSG_PLUS_SUFFIX 301
  20. #define TAG_MSG_WITH_LENGTH 302
  21. #define TAG_MSG_MASKING_KEY 303
  22. #define TAG_PAYLOAD_PREFIX 304
  23. #define TAG_PAYLOAD_LENGTH 305
  24. #define TAG_PAYLOAD_LENGTH16 306
  25. #define TAG_PAYLOAD_LENGTH64 307
  26. #define WS_OP_CONTINUATION_FRAME 0
  27. #define WS_OP_TEXT_FRAME 1
  28. #define WS_OP_BINARY_FRAME 2
  29. #define WS_OP_CONNECTION_CLOSE 8
  30. #define WS_OP_PING 9
  31. #define WS_OP_PONG 10
  32. static inline BOOL WS_OP_IS_FINAL_FRAGMENT(UInt8 frame)
  33. {
  34. return (frame & 0x80) ? YES : NO;
  35. }
  36. static inline BOOL WS_PAYLOAD_IS_MASKED(UInt8 frame)
  37. {
  38. return (frame & 0x80) ? YES : NO;
  39. }
  40. static inline NSUInteger WS_PAYLOAD_LENGTH(UInt8 frame)
  41. {
  42. return frame & 0x7F;
  43. }
  44. @interface WebSocket (PrivateAPI)
  45. - (void)readRequestBody;
  46. - (void)sendResponseBody;
  47. - (void)sendResponseHeaders;
  48. @end
  49. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  50. #pragma mark -
  51. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  52. @implementation WebSocket
  53. {
  54. BOOL isRFC6455;
  55. BOOL nextFrameMasked;
  56. NSUInteger nextOpCode;
  57. NSData *maskingKey;
  58. }
  59. + (BOOL)isWebSocketRequest:(HTTPMessage *)request
  60. {
  61. // Request (Draft 75):
  62. //
  63. // GET /demo HTTP/1.1
  64. // Upgrade: WebSocket
  65. // Connection: Upgrade
  66. // Host: example.com
  67. // Origin: http://example.com
  68. // WebSocket-Protocol: sample
  69. //
  70. //
  71. // Request (Draft 76):
  72. //
  73. // GET /demo HTTP/1.1
  74. // Upgrade: WebSocket
  75. // Connection: Upgrade
  76. // Host: example.com
  77. // Origin: http://example.com
  78. // Sec-WebSocket-Protocol: sample
  79. // Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5
  80. // Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
  81. //
  82. // ^n:ds[4U
  83. // Look for Upgrade: and Connection: headers.
  84. // If we find them, and they have the proper value,
  85. // we can safely assume this is a websocket request.
  86. NSString *upgradeHeaderValue = [request headerField:@"Upgrade"];
  87. NSString *connectionHeaderValue = [request headerField:@"Connection"];
  88. BOOL isWebSocket = YES;
  89. if (!upgradeHeaderValue || !connectionHeaderValue) {
  90. isWebSocket = NO;
  91. }
  92. else if (![upgradeHeaderValue caseInsensitiveCompare:@"WebSocket"] == NSOrderedSame) {
  93. isWebSocket = NO;
  94. }
  95. else if ([connectionHeaderValue rangeOfString:@"Upgrade" options:NSCaseInsensitiveSearch].location == NSNotFound) {
  96. isWebSocket = NO;
  97. }
  98. HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isWebSocket ? @"YES" : @"NO"));
  99. return isWebSocket;
  100. }
  101. + (BOOL)isVersion76Request:(HTTPMessage *)request
  102. {
  103. NSString *key1 = [request headerField:@"Sec-WebSocket-Key1"];
  104. NSString *key2 = [request headerField:@"Sec-WebSocket-Key2"];
  105. BOOL isVersion76;
  106. if (!key1 || !key2) {
  107. isVersion76 = NO;
  108. }
  109. else {
  110. isVersion76 = YES;
  111. }
  112. HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isVersion76 ? @"YES" : @"NO"));
  113. return isVersion76;
  114. }
  115. + (BOOL)isRFC6455Request:(HTTPMessage *)request
  116. {
  117. NSString *key = [request headerField:@"Sec-WebSocket-Key"];
  118. BOOL isRFC6455 = (key != nil);
  119. HTTPLogTrace2(@"%@: %@ - %@", THIS_FILE, THIS_METHOD, (isRFC6455 ? @"YES" : @"NO"));
  120. return isRFC6455;
  121. }
  122. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  123. #pragma mark Setup and Teardown
  124. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  125. @synthesize websocketQueue;
  126. - (id)initWithRequest:(HTTPMessage *)aRequest socket:(GCDAsyncSocket *)socket
  127. {
  128. HTTPLogTrace();
  129. if (aRequest == nil)
  130. {
  131. return nil;
  132. }
  133. if ((self = [super init]))
  134. {
  135. if (HTTP_LOG_VERBOSE)
  136. {
  137. NSData *requestHeaders = [aRequest messageData];
  138. NSString *temp = [[NSString alloc] initWithData:requestHeaders encoding:NSUTF8StringEncoding];
  139. HTTPLogVerbose(@"%@[%p] Request Headers:\n%@", THIS_FILE, self, temp);
  140. }
  141. websocketQueue = dispatch_queue_create("WebSocket", NULL);
  142. request = aRequest;
  143. asyncSocket = socket;
  144. [asyncSocket setDelegate:self delegateQueue:websocketQueue];
  145. isOpen = NO;
  146. isVersion76 = [[self class] isVersion76Request:request];
  147. isRFC6455 = [[self class] isRFC6455Request:request];
  148. term = [[NSData alloc] initWithBytes:"\xFF" length:1];
  149. }
  150. return self;
  151. }
  152. - (void)dealloc
  153. {
  154. HTTPLogTrace();
  155. dispatch_release(websocketQueue);
  156. [asyncSocket setDelegate:nil delegateQueue:NULL];
  157. [asyncSocket disconnect];
  158. }
  159. - (id)delegate
  160. {
  161. __block id result = nil;
  162. dispatch_sync(websocketQueue, ^{
  163. result = delegate;
  164. });
  165. return result;
  166. }
  167. - (void)setDelegate:(id)newDelegate
  168. {
  169. dispatch_async(websocketQueue, ^{
  170. delegate = newDelegate;
  171. });
  172. }
  173. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  174. #pragma mark Start and Stop
  175. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  176. /**
  177. * Starting point for the WebSocket after it has been fully initialized (including subclasses).
  178. * This method is called by the HTTPConnection it is spawned from.
  179. **/
  180. - (void)start
  181. {
  182. // This method is not exactly designed to be overriden.
  183. // Subclasses are encouraged to override the didOpen method instead.
  184. dispatch_async(websocketQueue, ^{ @autoreleasepool {
  185. if (isStarted) return;
  186. isStarted = YES;
  187. if (isVersion76)
  188. {
  189. [self readRequestBody];
  190. }
  191. else
  192. {
  193. [self sendResponseHeaders];
  194. [self didOpen];
  195. }
  196. }});
  197. }
  198. /**
  199. * This method is called by the HTTPServer if it is asked to stop.
  200. * The server, in turn, invokes stop on each WebSocket instance.
  201. **/
  202. - (void)stop
  203. {
  204. // This method is not exactly designed to be overriden.
  205. // Subclasses are encouraged to override the didClose method instead.
  206. dispatch_async(websocketQueue, ^{ @autoreleasepool {
  207. [asyncSocket disconnect];
  208. }});
  209. }
  210. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  211. #pragma mark HTTP Response
  212. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  213. - (void)readRequestBody
  214. {
  215. HTTPLogTrace();
  216. NSAssert(isVersion76, @"WebSocket version 75 doesn't contain a request body");
  217. [asyncSocket readDataToLength:8 withTimeout:TIMEOUT_NONE tag:TAG_HTTP_REQUEST_BODY];
  218. }
  219. - (NSString *)originResponseHeaderValue
  220. {
  221. HTTPLogTrace();
  222. NSString *origin = [request headerField:@"Origin"];
  223. if (origin == nil)
  224. {
  225. NSString *port = [NSString stringWithFormat:@"%hu", [asyncSocket localPort]];
  226. return [NSString stringWithFormat:@"http://localhost:%@", port];
  227. }
  228. else
  229. {
  230. return origin;
  231. }
  232. }
  233. - (NSString *)locationResponseHeaderValue
  234. {
  235. HTTPLogTrace();
  236. NSString *location;
  237. NSString *scheme = [asyncSocket isSecure] ? @"wss" : @"ws";
  238. NSString *host = [request headerField:@"Host"];
  239. NSString *requestUri = [[request url] relativeString];
  240. if (host == nil)
  241. {
  242. NSString *port = [NSString stringWithFormat:@"%hu", [asyncSocket localPort]];
  243. location = [NSString stringWithFormat:@"%@://localhost:%@%@", scheme, port, requestUri];
  244. }
  245. else
  246. {
  247. location = [NSString stringWithFormat:@"%@://%@%@", scheme, host, requestUri];
  248. }
  249. return location;
  250. }
  251. - (NSString *)secWebSocketKeyResponseHeaderValue {
  252. NSString *key = [request headerField: @"Sec-WebSocket-Key"];
  253. NSString *guid = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
  254. return [[key stringByAppendingString: guid] dataUsingEncoding: NSUTF8StringEncoding].sha1Digest.base64Encoded;
  255. }
  256. - (void)sendResponseHeaders
  257. {
  258. HTTPLogTrace();
  259. // Request (Draft 75):
  260. //
  261. // GET /demo HTTP/1.1
  262. // Upgrade: WebSocket
  263. // Connection: Upgrade
  264. // Host: example.com
  265. // Origin: http://example.com
  266. // WebSocket-Protocol: sample
  267. //
  268. //
  269. // Request (Draft 76):
  270. //
  271. // GET /demo HTTP/1.1
  272. // Upgrade: WebSocket
  273. // Connection: Upgrade
  274. // Host: example.com
  275. // Origin: http://example.com
  276. // Sec-WebSocket-Protocol: sample
  277. // Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
  278. // Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5
  279. //
  280. // ^n:ds[4U
  281. // Response (Draft 75):
  282. //
  283. // HTTP/1.1 101 Web Socket Protocol Handshake
  284. // Upgrade: WebSocket
  285. // Connection: Upgrade
  286. // WebSocket-Origin: http://example.com
  287. // WebSocket-Location: ws://example.com/demo
  288. // WebSocket-Protocol: sample
  289. //
  290. //
  291. // Response (Draft 76):
  292. //
  293. // HTTP/1.1 101 WebSocket Protocol Handshake
  294. // Upgrade: WebSocket
  295. // Connection: Upgrade
  296. // Sec-WebSocket-Origin: http://example.com
  297. // Sec-WebSocket-Location: ws://example.com/demo
  298. // Sec-WebSocket-Protocol: sample
  299. //
  300. // 8jKS'y:G*Co,Wxa-
  301. HTTPMessage *wsResponse = [[HTTPMessage alloc] initResponseWithStatusCode:101
  302. description:@"Web Socket Protocol Handshake"
  303. version:HTTPVersion1_1];
  304. [wsResponse setHeaderField:@"Upgrade" value:@"WebSocket"];
  305. [wsResponse setHeaderField:@"Connection" value:@"Upgrade"];
  306. // Note: It appears that WebSocket-Origin and WebSocket-Location
  307. // are required for Google's Chrome implementation to work properly.
  308. //
  309. // If we don't send either header, Chrome will never report the WebSocket as open.
  310. // If we only send one of the two, Chrome will immediately close the WebSocket.
  311. //
  312. // In addition to this it appears that Chrome's implementation is very picky of the values of the headers.
  313. // They have to match exactly with what Chrome sent us or it will close the WebSocket.
  314. NSString *originValue = [self originResponseHeaderValue];
  315. NSString *locationValue = [self locationResponseHeaderValue];
  316. NSString *originField = isVersion76 ? @"Sec-WebSocket-Origin" : @"WebSocket-Origin";
  317. NSString *locationField = isVersion76 ? @"Sec-WebSocket-Location" : @"WebSocket-Location";
  318. [wsResponse setHeaderField:originField value:originValue];
  319. [wsResponse setHeaderField:locationField value:locationValue];
  320. NSString *acceptValue = [self secWebSocketKeyResponseHeaderValue];
  321. if (acceptValue) {
  322. [wsResponse setHeaderField: @"Sec-WebSocket-Accept" value: acceptValue];
  323. }
  324. NSData *responseHeaders = [wsResponse messageData];
  325. if (HTTP_LOG_VERBOSE)
  326. {
  327. NSString *temp = [[NSString alloc] initWithData:responseHeaders encoding:NSUTF8StringEncoding];
  328. HTTPLogVerbose(@"%@[%p] Response Headers:\n%@", THIS_FILE, self, temp);
  329. }
  330. [asyncSocket writeData:responseHeaders withTimeout:TIMEOUT_NONE tag:TAG_HTTP_RESPONSE_HEADERS];
  331. }
  332. - (NSData *)processKey:(NSString *)key
  333. {
  334. HTTPLogTrace();
  335. unichar c;
  336. NSUInteger i;
  337. NSUInteger length = [key length];
  338. // Concatenate the digits into a string,
  339. // and count the number of spaces.
  340. NSMutableString *numStr = [NSMutableString stringWithCapacity:10];
  341. long long numSpaces = 0;
  342. for (i = 0; i < length; i++)
  343. {
  344. c = [key characterAtIndex:i];
  345. if (c >= '0' && c <= '9')
  346. {
  347. [numStr appendFormat:@"%C", c];
  348. }
  349. else if (c == ' ')
  350. {
  351. numSpaces++;
  352. }
  353. }
  354. long long num = strtoll([numStr UTF8String], NULL, 10);
  355. long long resultHostNum;
  356. if (numSpaces == 0)
  357. resultHostNum = 0;
  358. else
  359. resultHostNum = num / numSpaces;
  360. HTTPLogVerbose(@"key(%@) -> %qi / %qi = %qi", key, num, numSpaces, resultHostNum);
  361. // Convert result to 4 byte big-endian (network byte order)
  362. // and then convert to raw data.
  363. UInt32 result = OSSwapHostToBigInt32((uint32_t)resultHostNum);
  364. return [NSData dataWithBytes:&result length:4];
  365. }
  366. - (void)sendResponseBody:(NSData *)d3
  367. {
  368. HTTPLogTrace();
  369. NSAssert(isVersion76, @"WebSocket version 75 doesn't contain a response body");
  370. NSAssert([d3 length] == 8, @"Invalid requestBody length");
  371. NSString *key1 = [request headerField:@"Sec-WebSocket-Key1"];
  372. NSString *key2 = [request headerField:@"Sec-WebSocket-Key2"];
  373. NSData *d1 = [self processKey:key1];
  374. NSData *d2 = [self processKey:key2];
  375. // Concatenated d1, d2 & d3
  376. NSMutableData *d0 = [NSMutableData dataWithCapacity:(4+4+8)];
  377. [d0 appendData:d1];
  378. [d0 appendData:d2];
  379. [d0 appendData:d3];
  380. // Hash the data using MD5
  381. NSData *responseBody = [d0 md5Digest];
  382. [asyncSocket writeData:responseBody withTimeout:TIMEOUT_NONE tag:TAG_HTTP_RESPONSE_BODY];
  383. if (HTTP_LOG_VERBOSE)
  384. {
  385. NSString *s1 = [[NSString alloc] initWithData:d1 encoding:NSASCIIStringEncoding];
  386. NSString *s2 = [[NSString alloc] initWithData:d2 encoding:NSASCIIStringEncoding];
  387. NSString *s3 = [[NSString alloc] initWithData:d3 encoding:NSASCIIStringEncoding];
  388. NSString *s0 = [[NSString alloc] initWithData:d0 encoding:NSASCIIStringEncoding];
  389. NSString *sH = [[NSString alloc] initWithData:responseBody encoding:NSASCIIStringEncoding];
  390. HTTPLogVerbose(@"key1 result : raw(%@) str(%@)", d1, s1);
  391. HTTPLogVerbose(@"key2 result : raw(%@) str(%@)", d2, s2);
  392. HTTPLogVerbose(@"key3 passed : raw(%@) str(%@)", d3, s3);
  393. HTTPLogVerbose(@"key0 concat : raw(%@) str(%@)", d0, s0);
  394. HTTPLogVerbose(@"responseBody: raw(%@) str(%@)", responseBody, sH);
  395. }
  396. }
  397. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  398. #pragma mark Core Functionality
  399. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  400. - (void)didOpen
  401. {
  402. HTTPLogTrace();
  403. // Override me to perform any custom actions once the WebSocket has been opened.
  404. // This method is invoked on the websocketQueue.
  405. //
  406. // Don't forget to invoke [super didOpen] in your method.
  407. // Start reading for messages
  408. [asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:(isRFC6455 ? TAG_PAYLOAD_PREFIX : TAG_PREFIX)];
  409. // Notify delegate
  410. if ([delegate respondsToSelector:@selector(webSocketDidOpen:)])
  411. {
  412. [delegate webSocketDidOpen:self];
  413. }
  414. }
  415. - (void)sendMessage:(NSString *)msg
  416. {
  417. HTTPLogTrace();
  418. NSData *msgData = [msg dataUsingEncoding:NSUTF8StringEncoding];
  419. NSMutableData *data = nil;
  420. if (isRFC6455)
  421. {
  422. NSUInteger length = msgData.length;
  423. if (length <= 125)
  424. {
  425. data = [NSMutableData dataWithCapacity:(length + 2)];
  426. [data appendBytes: "\x81" length:1];
  427. UInt8 len = (UInt8)length;
  428. [data appendBytes: &len length:1];
  429. [data appendData:msgData];
  430. }
  431. else if (length <= 0xFFFF)
  432. {
  433. data = [NSMutableData dataWithCapacity:(length + 4)];
  434. [data appendBytes: "\x81\x7E" length:2];
  435. UInt16 len = (UInt16)length;
  436. [data appendBytes: (UInt8[]){len >> 8, len & 0xFF} length:2];
  437. [data appendData:msgData];
  438. }
  439. else
  440. {
  441. data = [NSMutableData dataWithCapacity:(length + 10)];
  442. [data appendBytes: "\x81\x7F" length:2];
  443. [data appendBytes: (UInt8[]){0, 0, 0, 0, (UInt8)(length >> 24), (UInt8)(length >> 16), (UInt8)(length >> 8), length & 0xFF} length:8];
  444. [data appendData:msgData];
  445. }
  446. }
  447. else
  448. {
  449. data = [NSMutableData dataWithCapacity:([msgData length] + 2)];
  450. [data appendBytes:"\x00" length:1];
  451. [data appendData:msgData];
  452. [data appendBytes:"\xFF" length:1];
  453. }
  454. // Remember: GCDAsyncSocket is thread-safe
  455. [asyncSocket writeData:data withTimeout:TIMEOUT_NONE tag:0];
  456. }
  457. - (void)didReceiveMessage:(NSString *)msg
  458. {
  459. HTTPLogTrace();
  460. // Override me to process incoming messages.
  461. // This method is invoked on the websocketQueue.
  462. //
  463. // For completeness, you should invoke [super didReceiveMessage:msg] in your method.
  464. // Notify delegate
  465. if ([delegate respondsToSelector:@selector(webSocket:didReceiveMessage:)])
  466. {
  467. [delegate webSocket:self didReceiveMessage:msg];
  468. }
  469. }
  470. - (void)didClose
  471. {
  472. HTTPLogTrace();
  473. // Override me to perform any cleanup when the socket is closed
  474. // This method is invoked on the websocketQueue.
  475. //
  476. // Don't forget to invoke [super didClose] at the end of your method.
  477. // Notify delegate
  478. if ([delegate respondsToSelector:@selector(webSocketDidClose:)])
  479. {
  480. [delegate webSocketDidClose:self];
  481. }
  482. // Notify HTTPServer
  483. [[NSNotificationCenter defaultCenter] postNotificationName:WebSocketDidDieNotification object:self];
  484. }
  485. #pragma mark WebSocket Frame
  486. - (BOOL)isValidWebSocketFrame:(UInt8)frame
  487. {
  488. NSUInteger rsv = frame & 0x70;
  489. NSUInteger opcode = frame & 0x0F;
  490. if (rsv || (3 <= opcode && opcode <= 7) || (0xB <= opcode && opcode <= 0xF))
  491. {
  492. return NO;
  493. }
  494. return YES;
  495. }
  496. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  497. #pragma mark AsyncSocket Delegate
  498. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  499. // 0 1 2 3
  500. // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  501. // +-+-+-+-+-------+-+-------------+-------------------------------+
  502. // |F|R|R|R| opcode|M| Payload len | Extended payload length |
  503. // |I|S|S|S| (4) |A| (7) | (16/64) |
  504. // |N|V|V|V| |S| | (if payload len==126/127) |
  505. // | |1|2|3| |K| | |
  506. // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
  507. // | Extended payload length continued, if payload len == 127 |
  508. // + - - - - - - - - - - - - - - - +-------------------------------+
  509. // | |Masking-key, if MASK set to 1 |
  510. // +-------------------------------+-------------------------------+
  511. // | Masking-key (continued) | Payload Data |
  512. // +-------------------------------- - - - - - - - - - - - - - - - +
  513. // : Payload Data continued ... :
  514. // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
  515. // | Payload Data continued ... |
  516. // +---------------------------------------------------------------+
  517. - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
  518. {
  519. HTTPLogTrace();
  520. if (tag == TAG_HTTP_REQUEST_BODY)
  521. {
  522. [self sendResponseHeaders];
  523. [self sendResponseBody:data];
  524. [self didOpen];
  525. }
  526. else if (tag == TAG_PREFIX)
  527. {
  528. UInt8 *pFrame = (UInt8 *)[data bytes];
  529. UInt8 frame = *pFrame;
  530. if (frame <= 0x7F)
  531. {
  532. [asyncSocket readDataToData:term withTimeout:TIMEOUT_NONE tag:TAG_MSG_PLUS_SUFFIX];
  533. }
  534. else
  535. {
  536. // Unsupported frame type
  537. [self didClose];
  538. }
  539. }
  540. else if (tag == TAG_PAYLOAD_PREFIX)
  541. {
  542. UInt8 *pFrame = (UInt8 *)[data bytes];
  543. UInt8 frame = *pFrame;
  544. if ([self isValidWebSocketFrame: frame])
  545. {
  546. nextOpCode = (frame & 0x0F);
  547. [asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH];
  548. }
  549. else
  550. {
  551. // Unsupported frame type
  552. [self didClose];
  553. }
  554. }
  555. else if (tag == TAG_PAYLOAD_LENGTH)
  556. {
  557. UInt8 frame = *(UInt8 *)[data bytes];
  558. BOOL masked = WS_PAYLOAD_IS_MASKED(frame);
  559. NSUInteger length = WS_PAYLOAD_LENGTH(frame);
  560. nextFrameMasked = masked;
  561. maskingKey = nil;
  562. if (length <= 125)
  563. {
  564. if (nextFrameMasked)
  565. {
  566. [asyncSocket readDataToLength:4 withTimeout:TIMEOUT_NONE tag:TAG_MSG_MASKING_KEY];
  567. }
  568. [asyncSocket readDataToLength:length withTimeout:TIMEOUT_NONE tag:TAG_MSG_WITH_LENGTH];
  569. }
  570. else if (length == 126)
  571. {
  572. [asyncSocket readDataToLength:2 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH16];
  573. }
  574. else
  575. {
  576. [asyncSocket readDataToLength:8 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_LENGTH64];
  577. }
  578. }
  579. else if (tag == TAG_PAYLOAD_LENGTH16)
  580. {
  581. UInt8 *pFrame = (UInt8 *)[data bytes];
  582. NSUInteger length = ((NSUInteger)pFrame[0] << 8) | (NSUInteger)pFrame[1];
  583. if (nextFrameMasked) {
  584. [asyncSocket readDataToLength:4 withTimeout:TIMEOUT_NONE tag:TAG_MSG_MASKING_KEY];
  585. }
  586. [asyncSocket readDataToLength:length withTimeout:TIMEOUT_NONE tag:TAG_MSG_WITH_LENGTH];
  587. }
  588. else if (tag == TAG_PAYLOAD_LENGTH64)
  589. {
  590. // FIXME: 64bit data size in memory?
  591. [self didClose];
  592. }
  593. else if (tag == TAG_MSG_WITH_LENGTH)
  594. {
  595. NSUInteger msgLength = [data length];
  596. if (nextFrameMasked && maskingKey) {
  597. NSMutableData *masked = data.mutableCopy;
  598. UInt8 *pData = (UInt8 *)masked.mutableBytes;
  599. UInt8 *pMask = (UInt8 *)maskingKey.bytes;
  600. for (NSUInteger i = 0; i < msgLength; i++)
  601. {
  602. pData[i] = pData[i] ^ pMask[i % 4];
  603. }
  604. data = masked;
  605. }
  606. if (nextOpCode == WS_OP_TEXT_FRAME)
  607. {
  608. NSString *msg = [[NSString alloc] initWithBytes:[data bytes] length:msgLength encoding:NSUTF8StringEncoding];
  609. [self didReceiveMessage:msg];
  610. }
  611. else
  612. {
  613. [self didClose];
  614. return;
  615. }
  616. // Read next frame
  617. [asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PAYLOAD_PREFIX];
  618. }
  619. else if (tag == TAG_MSG_MASKING_KEY)
  620. {
  621. maskingKey = data.copy;
  622. }
  623. else
  624. {
  625. NSUInteger msgLength = [data length] - 1; // Excluding ending 0xFF frame
  626. NSString *msg = [[NSString alloc] initWithBytes:[data bytes] length:msgLength encoding:NSUTF8StringEncoding];
  627. [self didReceiveMessage:msg];
  628. // Read next message
  629. [asyncSocket readDataToLength:1 withTimeout:TIMEOUT_NONE tag:TAG_PREFIX];
  630. }
  631. }
  632. - (void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)error
  633. {
  634. HTTPLogTrace2(@"%@[%p]: socketDidDisconnect:withError: %@", THIS_FILE, self, error);
  635. [self didClose];
  636. }
  637. @end