2222
2323#define TAG_PREFIX 300
2424#define TAG_MSG_PLUS_SUFFIX 301
25+ #define TAG_MSG_WITH_LENGTH 302
26+ #define TAG_MSG_MASKING_KEY 303
27+ #define TAG_PAYLOAD_PREFIX 304
28+ #define TAG_PAYLOAD_LENGTH 305
29+ #define TAG_PAYLOAD_LENGTH16 306
30+ #define TAG_PAYLOAD_LENGTH64 307
31+
32+ #define WS_OP_CONTINUATION_FRAME 0
33+ #define WS_OP_TEXT_FRAME 1
34+ #define WS_OP_BINARY_FRAME 2
35+ #define WS_OP_CONNECTION_CLOSE 8
36+ #define WS_OP_PING 9
37+ #define WS_OP_PONG 10
38+
39+ static inline BOOL WS_OP_IS_FINAL_FRAGMENT (UInt8 frame)
40+ {
41+ return (frame & 0x80 ) ? YES : NO ;
42+ }
43+
44+ static inline BOOL WS_PAYLOAD_IS_MASKED (UInt8 frame)
45+ {
46+ return (frame & 0x80 ) ? YES : NO ;
47+ }
2548
49+ static inline NSUInteger WS_PAYLOAD_LENGTH (UInt8 frame)
50+ {
51+ return frame & 0x7F ;
52+ }
2653
2754@interface WebSocket (PrivateAPI)
2855
@@ -37,6 +64,12 @@ - (void)sendResponseHeaders;
3764// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3865
3966@implementation WebSocket
67+ {
68+ BOOL isRFC6455;
69+ BOOL nextFrameMasked;
70+ NSUInteger nextOpCode;
71+ NSData *maskingKey;
72+ }
4073
4174+ (BOOL )isWebSocketRequest : (HTTPMessage *)request
4275{
@@ -106,6 +139,16 @@ + (BOOL)isVersion76Request:(HTTPMessage *)request
106139 return isVersion76;
107140}
108141
142+ + (BOOL )isRFC6455Request : (HTTPMessage *)request
143+ {
144+ NSString *key = [request headerField: @" Sec-WebSocket-Key" ];
145+ BOOL isRFC6455 = (key != nil );
146+
147+ HTTPLogTrace2 (@" %@ : %@ - %@ " , THIS_FILE, THIS_METHOD, (isRFC6455 ? @" YES" : @" NO" ));
148+
149+ return isRFC6455;
150+ }
151+
109152// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
110153#pragma mark Setup and Teardown
111154// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -139,6 +182,7 @@ - (id)initWithRequest:(HTTPMessage *)aRequest socket:(GCDAsyncSocket *)socket
139182
140183 isOpen = NO ;
141184 isVersion76 = [[self class ] isVersion76Request: request];
185+ isRFC6455 = [[self class ] isRFC6455Request: request];
142186
143187 term = [[NSData alloc ] initWithBytes: " \xFF " length: 1 ];
144188 }
@@ -276,6 +320,12 @@ - (NSString *)locationResponseHeaderValue
276320 return location;
277321}
278322
323+ - (NSString *)secWebSocketKeyResponseHeaderValue {
324+ NSString *key = [request headerField: @" Sec-WebSocket-Key" ];
325+ NSString *guid = @" 258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ;
326+ return [[key stringByAppendingString: guid] dataUsingEncoding: NSUTF8StringEncoding].sha1Digest .base64Encoded ;
327+ }
328+
279329- (void )sendResponseHeaders
280330{
281331 HTTPLogTrace ();
@@ -351,6 +401,11 @@ - (void)sendResponseHeaders
351401 [wsResponse setHeaderField: originField value: originValue];
352402 [wsResponse setHeaderField: locationField value: locationValue];
353403
404+ NSString *acceptValue = [self secWebSocketKeyResponseHeaderValue ];
405+ if (acceptValue) {
406+ [wsResponse setHeaderField: @" Sec-WebSocket-Accept" value: acceptValue];
407+ }
408+
354409 NSData *responseHeaders = [wsResponse messageData ];
355410
356411
@@ -469,7 +524,7 @@ - (void)didOpen
469524 // Don't forget to invoke [super didOpen] in your method.
470525
471526 // Start reading for messages
472- [asyncSocket readDataToLength: 1 withTimeout: TIMEOUT_NONE tag: TAG_PREFIX];
527+ [asyncSocket readDataToLength: 1 withTimeout: TIMEOUT_NONE tag: (isRFC6455 ? TAG_PAYLOAD_PREFIX : TAG_PREFIX) ];
473528
474529 // Notify delegate
475530 if ([delegate respondsToSelector: @selector (webSocketDidOpen: )])
@@ -483,12 +538,43 @@ - (void)sendMessage:(NSString *)msg
483538 HTTPLogTrace ();
484539
485540 NSData *msgData = [msg dataUsingEncoding: NSUTF8StringEncoding];
541+ NSMutableData *data = nil ;
486542
487- NSMutableData *data = [NSMutableData dataWithCapacity: ([msgData length ] + 2 )];
488-
489- [data appendBytes: " \x00 " length: 1 ];
490- [data appendData: msgData];
491- [data appendBytes: " \xFF " length: 1 ];
543+ if (isRFC6455)
544+ {
545+ NSUInteger length = msgData.length ;
546+ if (length <= 125 )
547+ {
548+ data = [NSMutableData dataWithCapacity: (length + 2 )];
549+ [data appendBytes: " \x81 " length: 1 ];
550+ UInt8 len = (UInt8)length;
551+ [data appendBytes: &len length: 1 ];
552+ [data appendData: msgData];
553+ }
554+ else if (length <= 0xFFFF )
555+ {
556+ data = [NSMutableData dataWithCapacity: (length + 4 )];
557+ [data appendBytes: " \x81\x7E " length: 2 ];
558+ UInt16 len = (UInt16)length;
559+ [data appendBytes: (UInt8[]){len >> 8 , len & 0xFF } length: 2 ];
560+ [data appendData: msgData];
561+ }
562+ else
563+ {
564+ data = [NSMutableData dataWithCapacity: (length + 10 )];
565+ [data appendBytes: " \x81\x7F " length: 2 ];
566+ [data appendBytes: (UInt8[]){0 , 0 , 0 , 0 , (UInt8)(length >> 24 ), (UInt8)(length >> 16 ), (UInt8)(length >> 8 ), length & 0xFF } length: 8 ];
567+ [data appendData: msgData];
568+ }
569+ }
570+ else
571+ {
572+ data = [NSMutableData dataWithCapacity: ([msgData length ] + 2 )];
573+
574+ [data appendBytes: " \x00 " length: 1 ];
575+ [data appendData: msgData];
576+ [data appendBytes: " \xFF " length: 1 ];
577+ }
492578
493579 // Remember: GCDAsyncSocket is thread-safe
494580
@@ -530,10 +616,42 @@ - (void)didClose
530616 [[NSNotificationCenter defaultCenter ] postNotificationName: WebSocketDidDieNotification object: self ];
531617}
532618
619+ #pragma mark WebSocket Frame
620+
621+ - (BOOL )isValidWebSocketFrame : (UInt8)frame
622+ {
623+ NSUInteger rsv = frame & 0x70 ;
624+ NSUInteger opcode = frame & 0x0F ;
625+ if (rsv || (3 <= opcode && opcode <= 7 ) || (0xB <= opcode && opcode <= 0xF ))
626+ {
627+ return NO ;
628+ }
629+ return YES ;
630+ }
631+
533632// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
534633#pragma mark AsyncSocket Delegate
535634// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
536635
636+ // 0 1 2 3
637+ // 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
638+ // +-+-+-+-+-------+-+-------------+-------------------------------+
639+ // |F|R|R|R| opcode|M| Payload len | Extended payload length |
640+ // |I|S|S|S| (4) |A| (7) | (16/64) |
641+ // |N|V|V|V| |S| | (if payload len==126/127) |
642+ // | |1|2|3| |K| | |
643+ // +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
644+ // | Extended payload length continued, if payload len == 127 |
645+ // + - - - - - - - - - - - - - - - +-------------------------------+
646+ // | |Masking-key, if MASK set to 1 |
647+ // +-------------------------------+-------------------------------+
648+ // | Masking-key (continued) | Payload Data |
649+ // +-------------------------------- - - - - - - - - - - - - - - - +
650+ // : Payload Data continued ... :
651+ // + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
652+ // | Payload Data continued ... |
653+ // +---------------------------------------------------------------+
654+
537655- (void )socket : (GCDAsyncSocket *)sock didReadData : (NSData *)data withTag : (long )tag
538656{
539657 HTTPLogTrace ();
@@ -559,6 +677,91 @@ - (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)t
559677 [self didClose ];
560678 }
561679 }
680+ else if (tag == TAG_PAYLOAD_PREFIX)
681+ {
682+ UInt8 *pFrame = (UInt8 *)[data bytes ];
683+ UInt8 frame = *pFrame;
684+
685+ if ([self isValidWebSocketFrame: frame])
686+ {
687+ nextOpCode = (frame & 0x0F );
688+ [asyncSocket readDataToLength: 1 withTimeout: TIMEOUT_NONE tag: TAG_PAYLOAD_LENGTH];
689+ }
690+ else
691+ {
692+ // Unsupported frame type
693+ [self didClose ];
694+ }
695+ }
696+ else if (tag == TAG_PAYLOAD_LENGTH)
697+ {
698+ UInt8 frame = *(UInt8 *)[data bytes ];
699+ BOOL masked = WS_PAYLOAD_IS_MASKED (frame);
700+ NSUInteger length = WS_PAYLOAD_LENGTH (frame);
701+ nextFrameMasked = masked;
702+ maskingKey = nil ;
703+ if (length <= 125 )
704+ {
705+ if (nextFrameMasked)
706+ {
707+ [asyncSocket readDataToLength: 4 withTimeout: TIMEOUT_NONE tag: TAG_MSG_MASKING_KEY];
708+ }
709+ [asyncSocket readDataToLength: length withTimeout: TIMEOUT_NONE tag: TAG_MSG_WITH_LENGTH];
710+ }
711+ else if (length == 126 )
712+ {
713+ [asyncSocket readDataToLength: 2 withTimeout: TIMEOUT_NONE tag: TAG_PAYLOAD_LENGTH16];
714+ }
715+ else
716+ {
717+ [asyncSocket readDataToLength: 8 withTimeout: TIMEOUT_NONE tag: TAG_PAYLOAD_LENGTH64];
718+ }
719+ }
720+ else if (tag == TAG_PAYLOAD_LENGTH16)
721+ {
722+ UInt8 *pFrame = (UInt8 *)[data bytes ];
723+ NSUInteger length = ((NSUInteger )pFrame[0 ] << 8 ) | (NSUInteger )pFrame[1 ];
724+ if (nextFrameMasked) {
725+ [asyncSocket readDataToLength: 4 withTimeout: TIMEOUT_NONE tag: TAG_MSG_MASKING_KEY];
726+ }
727+ [asyncSocket readDataToLength: length withTimeout: TIMEOUT_NONE tag: TAG_MSG_WITH_LENGTH];
728+ }
729+ else if (tag == TAG_PAYLOAD_LENGTH64)
730+ {
731+ // FIXME: 64bit data size in memory?
732+ [self didClose ];
733+ }
734+ else if (tag == TAG_MSG_WITH_LENGTH)
735+ {
736+ NSUInteger msgLength = [data length ];
737+ if (nextFrameMasked && maskingKey) {
738+ NSMutableData *masked = data.mutableCopy ;
739+ UInt8 *pData = (UInt8 *)masked.mutableBytes ;
740+ UInt8 *pMask = (UInt8 *)maskingKey.bytes ;
741+ for (NSUInteger i = 0 ; i < msgLength; i++)
742+ {
743+ pData[i] = pData[i] ^ pMask[i % 4 ];
744+ }
745+ data = masked;
746+ }
747+ if (nextOpCode == WS_OP_TEXT_FRAME)
748+ {
749+ NSString *msg = [[NSString alloc ] initWithBytes: [data bytes ] length: msgLength encoding: NSUTF8StringEncoding];
750+ [self didReceiveMessage: msg];
751+ }
752+ else
753+ {
754+ [self didClose ];
755+ return ;
756+ }
757+
758+ // Read next frame
759+ [asyncSocket readDataToLength: 1 withTimeout: TIMEOUT_NONE tag: TAG_PAYLOAD_PREFIX];
760+ }
761+ else if (tag == TAG_MSG_MASKING_KEY)
762+ {
763+ maskingKey = data.copy ;
764+ }
562765 else
563766 {
564767 NSUInteger msgLength = [data length ] - 1 ; // Excluding ending 0xFF frame
0 commit comments