WebSocket.m 24 KB

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