WebSocket.m 21 KB

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