Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(77)

Side by Side Diff: talk/examples/objc/AppRTCDemo/third_party/SocketRocket/SRWebSocket.m

Issue 1235563006: Move talk/examples/* to webrtc/examples. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: 201508051337 Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 //
2 // Copyright 2012 Square Inc.
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17
18 #import "SRWebSocket.h"
19
20 #if TARGET_OS_IPHONE
21 #define HAS_ICU
22 #endif
23
24 #ifdef HAS_ICU
25 #import <unicode/utf8.h>
26 #endif
27
28 #if TARGET_OS_IPHONE
29 #import <Endian.h>
30 #else
31 #import <CoreServices/CoreServices.h>
32 #endif
33
34 #import <CommonCrypto/CommonDigest.h>
35 #import <Security/SecRandom.h>
36
37 #if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
38 #define sr_dispatch_retain(x)
39 #define sr_dispatch_release(x)
40 #define maybe_bridge(x) ((__bridge void *) x)
41 #else
42 #define sr_dispatch_retain(x) dispatch_retain(x)
43 #define sr_dispatch_release(x) dispatch_release(x)
44 #define maybe_bridge(x) (x)
45 #endif
46
47 #if !__has_feature(objc_arc)
48 #error SocketRocket must be compiled with ARC enabled
49 #endif
50
51
52 typedef enum {
53 SROpCodeTextFrame = 0x1,
54 SROpCodeBinaryFrame = 0x2,
55 // 3-7 reserved.
56 SROpCodeConnectionClose = 0x8,
57 SROpCodePing = 0x9,
58 SROpCodePong = 0xA,
59 // B-F reserved.
60 } SROpCode;
61
62 typedef struct {
63 BOOL fin;
64 // BOOL rsv1;
65 // BOOL rsv2;
66 // BOOL rsv3;
67 uint8_t opcode;
68 BOOL masked;
69 uint64_t payload_length;
70 } frame_header;
71
72 static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95 CA-C5AB0DC85B11";
73
74 static inline int32_t validate_dispatch_data_partial_string(NSData *data);
75 static inline void SRFastLog(NSString *format, ...);
76
77 @interface NSData (SRWebSocket)
78
79 - (NSString *)stringBySHA1ThenBase64Encoding;
80
81 @end
82
83
84 @interface NSString (SRWebSocket)
85
86 - (NSString *)stringBySHA1ThenBase64Encoding;
87
88 @end
89
90
91 @interface NSURL (SRWebSocket)
92
93 // The origin isn't really applicable for a native application.
94 // So instead, just map ws -> http and wss -> https.
95 - (NSString *)SR_origin;
96
97 @end
98
99
100 @interface _SRRunLoopThread : NSThread
101
102 @property (nonatomic, readonly) NSRunLoop *runLoop;
103
104 @end
105
106
107 static NSString *newSHA1String(const char *bytes, size_t length) {
108 uint8_t md[CC_SHA1_DIGEST_LENGTH];
109
110 assert(length >= 0);
111 assert(length <= UINT32_MAX);
112 CC_SHA1(bytes, (CC_LONG)length, md);
113
114 NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH];
115
116 if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
117 return [data base64EncodedStringWithOptions:0];
118 }
119
120 return [data base64Encoding];
121 }
122
123 @implementation NSData (SRWebSocket)
124
125 - (NSString *)stringBySHA1ThenBase64Encoding;
126 {
127 return newSHA1String(self.bytes, self.length);
128 }
129
130 @end
131
132
133 @implementation NSString (SRWebSocket)
134
135 - (NSString *)stringBySHA1ThenBase64Encoding;
136 {
137 return newSHA1String(self.UTF8String, self.length);
138 }
139
140 @end
141
142 NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain";
143 NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode";
144
145 // Returns number of bytes consumed. Returning 0 means you didn't match.
146 // Sends bytes to callback handler;
147 typedef size_t (^stream_scanner)(NSData *collected_data);
148
149 typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data);
150
151 @interface SRIOConsumer : NSObject {
152 stream_scanner _scanner;
153 data_callback _handler;
154 size_t _bytesNeeded;
155 BOOL _readToCurrentFrame;
156 BOOL _unmaskBytes;
157 }
158 @property (nonatomic, copy, readonly) stream_scanner consumer;
159 @property (nonatomic, copy, readonly) data_callback handler;
160 @property (nonatomic, assign) size_t bytesNeeded;
161 @property (nonatomic, assign, readonly) BOOL readToCurrentFrame;
162 @property (nonatomic, assign, readonly) BOOL unmaskBytes;
163
164 @end
165
166 // This class is not thread-safe, and is expected to always be run on the same q ueue.
167 @interface SRIOConsumerPool : NSObject
168
169 - (id)initWithBufferCapacity:(NSUInteger)poolSize;
170
171 - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_call back)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurr entFrame unmaskBytes:(BOOL)unmaskBytes;
172 - (void)returnConsumer:(SRIOConsumer *)consumer;
173
174 @end
175
176 @interface SRWebSocket () <NSStreamDelegate>
177
178 - (void)_writeData:(NSData *)data;
179 - (void)_closeWithProtocolError:(NSString *)message;
180 - (void)_failWithError:(NSError *)error;
181
182 - (void)_disconnect;
183
184 - (void)_readFrameNew;
185 - (void)_readFrameContinue;
186
187 - (void)_pumpScanner;
188
189 - (void)_pumpWriting;
190
191 - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback )callback;
192 - (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)ca llback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes ;
193 - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback )callback dataLength:(size_t)dataLength;
194 - (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data _callback)dataHandler;
195 - (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler;
196
197 - (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data;
198
199 - (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage;
200 - (void)_SR_commonInit;
201
202 - (void)_initializeStreams;
203 - (void)_connect;
204
205 @property (nonatomic) SRReadyState readyState;
206
207 @property (nonatomic) NSOperationQueue *delegateOperationQueue;
208 @property (nonatomic) dispatch_queue_t delegateDispatchQueue;
209
210 @end
211
212
213 @implementation SRWebSocket {
214 NSInteger _webSocketVersion;
215
216 NSOperationQueue *_delegateOperationQueue;
217 dispatch_queue_t _delegateDispatchQueue;
218
219 dispatch_queue_t _workQueue;
220 NSMutableArray *_consumers;
221
222 NSInputStream *_inputStream;
223 NSOutputStream *_outputStream;
224
225 NSMutableData *_readBuffer;
226 NSUInteger _readBufferOffset;
227
228 NSMutableData *_outputBuffer;
229 NSUInteger _outputBufferOffset;
230
231 uint8_t _currentFrameOpcode;
232 size_t _currentFrameCount;
233 size_t _readOpCount;
234 uint32_t _currentStringScanPosition;
235 NSMutableData *_currentFrameData;
236
237 NSString *_closeReason;
238
239 NSString *_secKey;
240
241 BOOL _pinnedCertFound;
242
243 uint8_t _currentReadMaskKey[4];
244 size_t _currentReadMaskOffset;
245
246 BOOL _consumerStopped;
247
248 BOOL _closeWhenFinishedWriting;
249 BOOL _failed;
250
251 BOOL _secure;
252 NSURLRequest *_urlRequest;
253
254 CFHTTPMessageRef _receivedHTTPHeaders;
255
256 BOOL _sentClose;
257 BOOL _didFail;
258 int _closeCode;
259
260 BOOL _isPumping;
261
262 NSMutableSet *_scheduledRunloops;
263
264 // We use this to retain ourselves.
265 __strong SRWebSocket *_selfRetain;
266
267 NSArray *_requestedProtocols;
268 SRIOConsumerPool *_consumerPool;
269 }
270
271 @synthesize delegate = _delegate;
272 @synthesize url = _url;
273 @synthesize readyState = _readyState;
274 @synthesize protocol = _protocol;
275
276 static __strong NSData *CRLFCRLF;
277
278 + (void)initialize;
279 {
280 CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
281 }
282
283 - (id)initWithURLRequest:(NSURLRequest *)request protocols:(NSArray *)protocols;
284 {
285 self = [super init];
286 if (self) {
287 assert(request.URL);
288 _url = request.URL;
289 _urlRequest = request;
290
291 _requestedProtocols = [protocols copy];
292
293 [self _SR_commonInit];
294 }
295
296 return self;
297 }
298
299 - (id)initWithURLRequest:(NSURLRequest *)request;
300 {
301 return [self initWithURLRequest:request protocols:nil];
302 }
303
304 - (id)initWithURL:(NSURL *)url;
305 {
306 return [self initWithURL:url protocols:nil];
307 }
308
309 - (id)initWithURL:(NSURL *)url protocols:(NSArray *)protocols;
310 {
311 NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url] ;
312 return [self initWithURLRequest:request protocols:protocols];
313 }
314
315 - (void)_SR_commonInit;
316 {
317
318 NSString *scheme = _url.scheme.lowercaseString;
319 assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]);
320
321 if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) {
322 _secure = YES;
323 }
324
325 _readyState = SR_CONNECTING;
326 _consumerStopped = YES;
327 _webSocketVersion = 13;
328
329 _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
330
331 // Going to set a specific on the queue so we can validate we're on the work queue
332 dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge( _workQueue), NULL);
333
334 _delegateDispatchQueue = dispatch_get_main_queue();
335 sr_dispatch_retain(_delegateDispatchQueue);
336
337 _readBuffer = [[NSMutableData alloc] init];
338 _outputBuffer = [[NSMutableData alloc] init];
339
340 _currentFrameData = [[NSMutableData alloc] init];
341
342 _consumers = [[NSMutableArray alloc] init];
343
344 _consumerPool = [[SRIOConsumerPool alloc] init];
345
346 _scheduledRunloops = [[NSMutableSet alloc] init];
347
348 [self _initializeStreams];
349
350 // default handlers
351 }
352
353 - (void)assertOnWorkQueue;
354 {
355 assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQue ue));
356 }
357
358 - (void)dealloc
359 {
360 _inputStream.delegate = nil;
361 _outputStream.delegate = nil;
362
363 [_inputStream close];
364 [_outputStream close];
365
366 sr_dispatch_release(_workQueue);
367 _workQueue = NULL;
368
369 if (_receivedHTTPHeaders) {
370 CFRelease(_receivedHTTPHeaders);
371 _receivedHTTPHeaders = NULL;
372 }
373
374 if (_delegateDispatchQueue) {
375 sr_dispatch_release(_delegateDispatchQueue);
376 _delegateDispatchQueue = NULL;
377 }
378 }
379
380 #ifndef NDEBUG
381
382 - (void)setReadyState:(SRReadyState)aReadyState;
383 {
384 [self willChangeValueForKey:@"readyState"];
385 assert(aReadyState > _readyState);
386 _readyState = aReadyState;
387 [self didChangeValueForKey:@"readyState"];
388 }
389
390 #endif
391
392 - (void)open;
393 {
394 assert(_url);
395 NSAssert(_readyState == SR_CONNECTING, @"Cannot call -(void)open on SRWebSoc ket more than once");
396
397 _selfRetain = self;
398
399 [self _connect];
400 }
401
402 // Calls block on delegate queue
403 - (void)_performDelegateBlock:(dispatch_block_t)block;
404 {
405 if (_delegateOperationQueue) {
406 [_delegateOperationQueue addOperationWithBlock:block];
407 } else {
408 assert(_delegateDispatchQueue);
409 dispatch_async(_delegateDispatchQueue, block);
410 }
411 }
412
413 - (void)setDelegateDispatchQueue:(dispatch_queue_t)queue;
414 {
415 if (queue) {
416 sr_dispatch_retain(queue);
417 }
418
419 if (_delegateDispatchQueue) {
420 sr_dispatch_release(_delegateDispatchQueue);
421 }
422
423 _delegateDispatchQueue = queue;
424 }
425
426 - (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage;
427 {
428 NSString *acceptHeader = CFBridgingRelease(CFHTTPMessageCopyHeaderFieldValue (httpMessage, CFSTR("Sec-WebSocket-Accept")));
429
430 if (acceptHeader == nil) {
431 return NO;
432 }
433
434 NSString *concattedString = [_secKey stringByAppendingString:SRWebSocketAppe ndToSecKeyString];
435 NSString *expectedAccept = [concattedString stringBySHA1ThenBase64Encoding];
436
437 return [acceptHeader isEqualToString:expectedAccept];
438 }
439
440 - (void)_HTTPHeadersDidFinish;
441 {
442 NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHea ders);
443
444 if (responseCode >= 400) {
445 SRFastLog(@"Request failed with response code %d", responseCode);
446 [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain cod e:2132 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"receive d bad response code from server %ld", (long)responseCode], SRHTTPResponseErrorKe y:@(responseCode)}]];
447 return;
448 }
449
450 if(![self _checkHandshake:_receivedHTTPHeaders]) {
451 [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain cod e:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@" Invalid Sec-WebSocket-Accept response"] forKey:NSLocalizedDescriptionKey]]];
452 return;
453 }
454
455 NSString *negotiatedProtocol = CFBridgingRelease(CFHTTPMessageCopyHeaderFiel dValue(_receivedHTTPHeaders, CFSTR("Sec-WebSocket-Protocol")));
456 if (negotiatedProtocol) {
457 // Make sure we requested the protocol
458 if ([_requestedProtocols indexOfObject:negotiatedProtocol] == NSNotFound ) {
459 [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2133 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithForma t:@"Server specified Sec-WebSocket-Protocol that wasn't requested"] forKey:NSLoc alizedDescriptionKey]]];
460 return;
461 }
462
463 _protocol = negotiatedProtocol;
464 }
465
466 self.readyState = SR_OPEN;
467
468 if (!_didFail) {
469 [self _readFrameNew];
470 }
471
472 [self _performDelegateBlock:^{
473 if ([self.delegate respondsToSelector:@selector(webSocketDidOpen:)]) {
474 [self.delegate webSocketDidOpen:self];
475 };
476 }];
477 }
478
479
480 - (void)_readHTTPHeader;
481 {
482 if (_receivedHTTPHeaders == NULL) {
483 _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);
484 }
485
486 [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *dat a) {
487 CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes , data.length);
488
489 if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
490 SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMe ssageCopyAllHeaderFields(_receivedHTTPHeaders)));
491 [self _HTTPHeadersDidFinish];
492 } else {
493 [self _readHTTPHeader];
494 }
495 }];
496 }
497
498 - (void)didConnect
499 {
500 SRFastLog(@"Connected");
501 CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (_ _bridge CFURLRef)_url, kCFHTTPVersion1_1);
502
503 // Set host first so it defaults
504 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringR ef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _ur l.host));
505
506 NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
507 SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes );
508
509 if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)] ) {
510 _secKey = [keyBytes base64EncodedStringWithOptions:0];
511 } else {
512 _secKey = [keyBytes base64Encoding];
513 }
514
515 assert([_secKey length] == 24);
516
517 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket "));
518 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrad e"));
519 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bri dge CFStringRef)_secKey);
520 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (_ _bridge CFStringRef)[NSString stringWithFormat:@"%ld", (long)_webSocketVersion]) ;
521
522 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStrin gRef)_url.SR_origin);
523
524 if (_requestedProtocols) {
525 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Protocol" ), (__bridge CFStringRef)[_requestedProtocols componentsJoinedByString:@", "]);
526 }
527
528 [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
529 CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__ bridge CFStringRef)obj);
530 }];
531
532 NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(reque st));
533
534 CFRelease(request);
535
536 [self _writeData:message];
537 [self _readHTTPHeader];
538 }
539
540 - (void)_initializeStreams;
541 {
542 assert(_url.port.unsignedIntValue <= UINT32_MAX);
543 uint32_t port = _url.port.unsignedIntValue;
544 if (port == 0) {
545 if (!_secure) {
546 port = 80;
547 } else {
548 port = 443;
549 }
550 }
551 NSString *host = _url.host;
552
553 CFReadStreamRef readStream = NULL;
554 CFWriteStreamRef writeStream = NULL;
555
556 CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, & readStream, &writeStream);
557
558 _outputStream = CFBridgingRelease(writeStream);
559 _inputStream = CFBridgingRelease(readStream);
560
561
562 if (_secure) {
563 NSMutableDictionary *SSLOptions = [[NSMutableDictionary alloc] init];
564
565 [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNego tiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];
566
567 // If we're using pinned certs, don't validate the certificate chain
568 if ([_urlRequest SR_SSLPinnedCertificates].count) {
569 [SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge i d)kCFStreamSSLValidatesCertificateChain];
570 }
571
572 #if DEBUG
573 [SSLOptions setValue:[NSNumber numberWithBool:NO] forKey:(__bridge id)kC FStreamSSLValidatesCertificateChain];
574 NSLog(@"SocketRocket: In debug mode. Allowing connection to any root ce rt");
575 #endif
576
577 [_outputStream setProperty:SSLOptions
578 forKey:(__bridge id)kCFStreamPropertySSLSettings];
579 }
580
581 _inputStream.delegate = self;
582 _outputStream.delegate = self;
583 }
584
585 - (void)_connect;
586 {
587 if (!_scheduledRunloops.count) {
588 [self scheduleInRunLoop:[NSRunLoop SR_networkRunLoop] forMode:NSDefaultR unLoopMode];
589 }
590
591
592 [_outputStream open];
593 [_inputStream open];
594 }
595
596 - (void)scheduleInRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
597 {
598 [_outputStream scheduleInRunLoop:aRunLoop forMode:mode];
599 [_inputStream scheduleInRunLoop:aRunLoop forMode:mode];
600
601 [_scheduledRunloops addObject:@[aRunLoop, mode]];
602 }
603
604 - (void)unscheduleFromRunLoop:(NSRunLoop *)aRunLoop forMode:(NSString *)mode;
605 {
606 [_outputStream removeFromRunLoop:aRunLoop forMode:mode];
607 [_inputStream removeFromRunLoop:aRunLoop forMode:mode];
608
609 [_scheduledRunloops removeObject:@[aRunLoop, mode]];
610 }
611
612 - (void)close;
613 {
614 [self closeWithCode:SRStatusCodeNormal reason:nil];
615 }
616
617 - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
618 {
619 assert(code);
620 dispatch_async(_workQueue, ^{
621 if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) {
622 return;
623 }
624
625 BOOL wasConnecting = self.readyState == SR_CONNECTING;
626
627 self.readyState = SR_CLOSING;
628
629 SRFastLog(@"Closing with code %d reason %@", code, reason);
630
631 if (wasConnecting) {
632 [self _disconnect];
633 return;
634 }
635
636 size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8Stri ngEncoding];
637 NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:si zeof(uint16_t) + maxMsgSize];
638 NSData *payload = mutablePayload;
639
640 ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code);
641
642 if (reason) {
643 NSRange remainingRange = {0};
644
645 NSUInteger usedLength = 0;
646
647 BOOL success = [reason getBytes:(char *)mutablePayload.mutableBytes + sizeof(uint16_t) maxLength:payload.length - sizeof(uint16_t) usedLength:&usedL ength encoding:NSUTF8StringEncoding options:NSStringEncodingConversionExternalRe presentation range:NSMakeRange(0, reason.length) remainingRange:&remainingRange] ;
648
649 assert(success);
650 assert(remainingRange.length == 0);
651
652 if (usedLength != maxMsgSize) {
653 payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))];
654 }
655 }
656
657
658 [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload];
659 });
660 }
661
662 - (void)_closeWithProtocolError:(NSString *)message;
663 {
664 // Need to shunt this on the _callbackQueue first to see if they received an y messages
665 [self _performDelegateBlock:^{
666 [self closeWithCode:SRStatusCodeProtocolError reason:message];
667 dispatch_async(_workQueue, ^{
668 [self _disconnect];
669 });
670 }];
671 }
672
673 - (void)_failWithError:(NSError *)error;
674 {
675 dispatch_async(_workQueue, ^{
676 if (self.readyState != SR_CLOSED) {
677 _failed = YES;
678 [self _performDelegateBlock:^{
679 if ([self.delegate respondsToSelector:@selector(webSocket:didFai lWithError:)]) {
680 [self.delegate webSocket:self didFailWithError:error];
681 }
682 }];
683
684 self.readyState = SR_CLOSED;
685 _selfRetain = nil;
686
687 SRFastLog(@"Failing with error %@", error.localizedDescription);
688
689 [self _disconnect];
690 }
691 });
692 }
693
694 - (void)_writeData:(NSData *)data;
695 {
696 [self assertOnWorkQueue];
697
698 if (_closeWhenFinishedWriting) {
699 return;
700 }
701 [_outputBuffer appendData:data];
702 [self _pumpWriting];
703 }
704
705 - (void)send:(id)data;
706 {
707 NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send : until connection is open");
708 // TODO: maybe not copy this for performance
709 data = [data copy];
710 dispatch_async(_workQueue, ^{
711 if ([data isKindOfClass:[NSString class]]) {
712 [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]];
713 } else if ([data isKindOfClass:[NSData class]]) {
714 [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data];
715 } else if (data == nil) {
716 [self _sendFrameWithOpcode:SROpCodeTextFrame data:data];
717 } else {
718 assert(NO);
719 }
720 });
721 }
722
723 - (void)sendPing:(NSData *)data;
724 {
725 NSAssert(self.readyState == SR_OPEN, @"Invalid State: Cannot call send: unti l connection is open");
726 // TODO: maybe not copy this for performance
727 data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty
728 dispatch_async(_workQueue, ^{
729 [self _sendFrameWithOpcode:SROpCodePing data:data];
730 });
731 }
732
733 - (void)handlePing:(NSData *)pingData;
734 {
735 // Need to pingpong this off _callbackQueue first to make sure messages happ en in order
736 [self _performDelegateBlock:^{
737 dispatch_async(_workQueue, ^{
738 [self _sendFrameWithOpcode:SROpCodePong data:pingData];
739 });
740 }];
741 }
742
743 - (void)handlePong:(NSData *)pongData;
744 {
745 SRFastLog(@"Received pong");
746 [self _performDelegateBlock:^{
747 if ([self.delegate respondsToSelector:@selector(webSocket:didReceivePong :)]) {
748 [self.delegate webSocket:self didReceivePong:pongData];
749 }
750 }];
751 }
752
753 - (void)_handleMessage:(id)message
754 {
755 SRFastLog(@"Received message");
756 [self _performDelegateBlock:^{
757 [self.delegate webSocket:self didReceiveMessage:message];
758 }];
759 }
760
761
762 static inline BOOL closeCodeIsValid(int closeCode) {
763 if (closeCode < 1000) {
764 return NO;
765 }
766
767 if (closeCode >= 1000 && closeCode <= 1011) {
768 if (closeCode == 1004 ||
769 closeCode == 1005 ||
770 closeCode == 1006) {
771 return NO;
772 }
773 return YES;
774 }
775
776 if (closeCode >= 3000 && closeCode <= 3999) {
777 return YES;
778 }
779
780 if (closeCode >= 4000 && closeCode <= 4999) {
781 return YES;
782 }
783
784 return NO;
785 }
786
787 // Note from RFC:
788 //
789 // If there is a body, the first two
790 // bytes of the body MUST be a 2-byte unsigned integer (in network byte
791 // order) representing a status code with value /code/ defined in
792 // Section 7.4. Following the 2-byte integer the body MAY contain UTF-8
793 // encoded data with value /reason/, the interpretation of which is not
794 // defined by this specification.
795
796 - (void)handleCloseWithData:(NSData *)data;
797 {
798 size_t dataSize = data.length;
799 __block uint16_t closeCode = 0;
800
801 SRFastLog(@"Received close frame");
802
803 if (dataSize == 1) {
804 // TODO handle error
805 [self _closeWithProtocolError:@"Payload for close must be larger than 2 bytes"];
806 return;
807 } else if (dataSize >= 2) {
808 [data getBytes:&closeCode length:sizeof(closeCode)];
809 _closeCode = EndianU16_BtoN(closeCode);
810 if (!closeCodeIsValid(_closeCode)) {
811 [self _closeWithProtocolError:[NSString stringWithFormat:@"Cannot ha ve close code of %d", _closeCode]];
812 return;
813 }
814 if (dataSize > 2) {
815 _closeReason = [[NSString alloc] initWithData:[data subdataWithRange :NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding];
816 if (!_closeReason) {
817 [self _closeWithProtocolError:@"Close reason MUST be valid UTF-8 "];
818 return;
819 }
820 }
821 } else {
822 _closeCode = SRStatusNoStatusReceived;
823 }
824
825 [self assertOnWorkQueue];
826
827 if (self.readyState == SR_OPEN) {
828 [self closeWithCode:1000 reason:nil];
829 }
830 dispatch_async(_workQueue, ^{
831 [self _disconnect];
832 });
833 }
834
835 - (void)_disconnect;
836 {
837 [self assertOnWorkQueue];
838 SRFastLog(@"Trying to disconnect");
839 _closeWhenFinishedWriting = YES;
840 [self _pumpWriting];
841 }
842
843 - (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode;
844 {
845 // Check that the current data is valid UTF8
846
847 BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || o pcode == SROpCodeConnectionClose);
848 if (!isControlFrame) {
849 [self _readFrameNew];
850 } else {
851 dispatch_async(_workQueue, ^{
852 [self _readFrameContinue];
853 });
854 }
855
856 switch (opcode) {
857 case SROpCodeTextFrame: {
858 NSString *str = [[NSString alloc] initWithData:frameData encoding:NS UTF8StringEncoding];
859 if (str == nil && frameData) {
860 [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"];
861 dispatch_async(_workQueue, ^{
862 [self _disconnect];
863 });
864
865 return;
866 }
867 [self _handleMessage:str];
868 break;
869 }
870 case SROpCodeBinaryFrame:
871 [self _handleMessage:[frameData copy]];
872 break;
873 case SROpCodeConnectionClose:
874 [self handleCloseWithData:frameData];
875 break;
876 case SROpCodePing:
877 [self handlePing:frameData];
878 break;
879 case SROpCodePong:
880 [self handlePong:frameData];
881 break;
882 default:
883 [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown o pcode %ld", (long)opcode]];
884 // TODO: Handle invalid opcode
885 break;
886 }
887 }
888
889 - (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData;
890 {
891 assert(frame_header.opcode != 0);
892
893 if (self.readyState != SR_OPEN) {
894 return;
895 }
896
897
898 BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.o pcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose);
899
900 if (isControlFrame && !frame_header.fin) {
901 [self _closeWithProtocolError:@"Fragmented control frames not allowed"];
902 return;
903 }
904
905 if (isControlFrame && frame_header.payload_length >= 126) {
906 [self _closeWithProtocolError:@"Control frames cannot have payloads larg er than 126 bytes"];
907 return;
908 }
909
910 if (!isControlFrame) {
911 _currentFrameOpcode = frame_header.opcode;
912 _currentFrameCount += 1;
913 }
914
915 if (frame_header.payload_length == 0) {
916 if (isControlFrame) {
917 [self _handleFrameWithData:curData opCode:frame_header.opcode];
918 } else {
919 if (frame_header.fin) {
920 [self _handleFrameWithData:_currentFrameData opCode:frame_header .opcode];
921 } else {
922 // TODO add assert that opcode is not a control;
923 [self _readFrameContinue];
924 }
925 }
926 } else {
927 assert(frame_header.payload_length <= SIZE_T_MAX);
928 [self _addConsumerWithDataLength:(size_t)frame_header.payload_length cal lback:^(SRWebSocket *self, NSData *newData) {
929 if (isControlFrame) {
930 [self _handleFrameWithData:newData opCode:frame_header.opcode];
931 } else {
932 if (frame_header.fin) {
933 [self _handleFrameWithData:self->_currentFrameData opCode:fr ame_header.opcode];
934 } else {
935 // TODO add assert that opcode is not a control;
936 [self _readFrameContinue];
937 }
938
939 }
940 } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked];
941 }
942 }
943
944 /* From RFC:
945
946 0 1 2 3
947 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
948 +-+-+-+-+-------+-+-------------+-------------------------------+
949 |F|R|R|R| opcode|M| Payload len | Extended payload length |
950 |I|S|S|S| (4) |A| (7) | (16/64) |
951 |N|V|V|V| |S| | (if payload len==126/127) |
952 | |1|2|3| |K| | |
953 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
954 | Extended payload length continued, if payload len == 127 |
955 + - - - - - - - - - - - - - - - +-------------------------------+
956 | |Masking-key, if MASK set to 1 |
957 +-------------------------------+-------------------------------+
958 | Masking-key (continued) | Payload Data |
959 +-------------------------------- - - - - - - - - - - - - - - - +
960 : Payload Data continued ... :
961 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
962 | Payload Data continued ... |
963 +---------------------------------------------------------------+
964 */
965
966 static const uint8_t SRFinMask = 0x80;
967 static const uint8_t SROpCodeMask = 0x0F;
968 static const uint8_t SRRsvMask = 0x70;
969 static const uint8_t SRMaskMask = 0x80;
970 static const uint8_t SRPayloadLenMask = 0x7F;
971
972
973 - (void)_readFrameContinue;
974 {
975 assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFra meCount > 0 && _currentFrameOpcode > 0));
976
977 [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *dat a) {
978 __block frame_header header = {0};
979
980 const uint8_t *headerBuffer = data.bytes;
981 assert(data.length >= 2);
982
983 if (headerBuffer[0] & SRRsvMask) {
984 [self _closeWithProtocolError:@"Server used RSV bits"];
985 return;
986 }
987
988 uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]);
989
990 BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose);
991
992 if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) {
993 [self _closeWithProtocolError:@"all data frames after the initial da ta frame must have opcode 0"];
994 return;
995 }
996
997 if (receivedOpcode == 0 && self->_currentFrameCount == 0) {
998 [self _closeWithProtocolError:@"cannot continue a message"];
999 return;
1000 }
1001
1002 header.opcode = receivedOpcode == 0 ? self->_currentFrameOpcode : receiv edOpcode;
1003
1004 header.fin = !!(SRFinMask & headerBuffer[0]);
1005
1006
1007 header.masked = !!(SRMaskMask & headerBuffer[1]);
1008 header.payload_length = SRPayloadLenMask & headerBuffer[1];
1009
1010 headerBuffer = NULL;
1011
1012 if (header.masked) {
1013 [self _closeWithProtocolError:@"Client must receive unmasked data"];
1014 }
1015
1016 size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0;
1017
1018 if (header.payload_length == 126) {
1019 extra_bytes_needed += sizeof(uint16_t);
1020 } else if (header.payload_length == 127) {
1021 extra_bytes_needed += sizeof(uint64_t);
1022 }
1023
1024 if (extra_bytes_needed == 0) {
1025 [self _handleFrameHeader:header curData:self->_currentFrameData];
1026 } else {
1027 [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWeb Socket *self, NSData *data) {
1028 size_t mapped_size = data.length;
1029 const void *mapped_buffer = data.bytes;
1030 size_t offset = 0;
1031
1032 if (header.payload_length == 126) {
1033 assert(mapped_size >= sizeof(uint16_t));
1034 uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer ));
1035 header.payload_length = newLen;
1036 offset += sizeof(uint16_t);
1037 } else if (header.payload_length == 127) {
1038 assert(mapped_size >= sizeof(uint64_t));
1039 header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_ buffer));
1040 offset += sizeof(uint64_t);
1041 } else {
1042 assert(header.payload_length < 126 && header.payload_length >= 0);
1043 }
1044
1045
1046 if (header.masked) {
1047 assert(mapped_size >= sizeof(_currentReadMaskOffset) + offse t);
1048 memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey));
1049 }
1050
1051 [self _handleFrameHeader:header curData:self->_currentFrameData] ;
1052 } readToCurrentFrame:NO unmaskBytes:NO];
1053 }
1054 } readToCurrentFrame:NO unmaskBytes:NO];
1055 }
1056
1057 - (void)_readFrameNew;
1058 {
1059 dispatch_async(_workQueue, ^{
1060 [_currentFrameData setLength:0];
1061
1062 _currentFrameOpcode = 0;
1063 _currentFrameCount = 0;
1064 _readOpCount = 0;
1065 _currentStringScanPosition = 0;
1066
1067 [self _readFrameContinue];
1068 });
1069 }
1070
1071 - (void)_pumpWriting;
1072 {
1073 [self assertOnWorkQueue];
1074
1075 NSUInteger dataLength = _outputBuffer.length;
1076 if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) {
1077 NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _out putBufferOffset maxLength:dataLength - _outputBufferOffset];
1078 if (bytesWritten == -1) {
1079 [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream " forKey:NSLocalizedDescriptionKey]]];
1080 return;
1081 }
1082
1083 _outputBufferOffset += bytesWritten;
1084
1085 if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.l ength >> 1)) {
1086 _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_output Buffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOf fset];
1087 _outputBufferOffset = 0;
1088 }
1089 }
1090
1091 if (_closeWhenFinishedWriting &&
1092 _outputBuffer.length - _outputBufferOffset == 0 &&
1093 (_inputStream.streamStatus != NSStreamStatusNotOpen &&
1094 _inputStream.streamStatus != NSStreamStatusClosed) &&
1095 !_sentClose) {
1096 _sentClose = YES;
1097
1098 [_outputStream close];
1099 [_inputStream close];
1100
1101
1102 for (NSArray *runLoop in [_scheduledRunloops copy]) {
1103 [self unscheduleFromRunLoop:[runLoop objectAtIndex:0] forMode:[runLo op objectAtIndex:1]];
1104 }
1105
1106 if (!_failed) {
1107 [self _performDelegateBlock:^{
1108 if ([self.delegate respondsToSelector:@selector(webSocket:didClo seWithCode:reason:wasClean:)]) {
1109 [self.delegate webSocket:self didCloseWithCode:_closeCode re ason:_closeReason wasClean:YES];
1110 }
1111 }];
1112 }
1113
1114 _selfRetain = nil;
1115 }
1116 }
1117
1118 - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback )callback;
1119 {
1120 [self assertOnWorkQueue];
1121 [self _addConsumerWithScanner:consumer callback:callback dataLength:0];
1122 }
1123
1124 - (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)ca llback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes ;
1125 {
1126 [self assertOnWorkQueue];
1127 assert(dataLength);
1128
1129 [_consumers addObject:[_consumerPool consumerWithScanner:nil handler:callbac k bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmas kBytes]];
1130 [self _pumpScanner];
1131 }
1132
1133 - (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback )callback dataLength:(size_t)dataLength;
1134 {
1135 [self assertOnWorkQueue];
1136 [_consumers addObject:[_consumerPool consumerWithScanner:consumer handler:ca llback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]];
1137 [self _pumpScanner];
1138 }
1139
1140
1141 static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
1142
1143 - (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler;
1144 {
1145 [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:da taHandler];
1146 }
1147
1148 - (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data _callback)dataHandler;
1149 {
1150 // TODO optimize so this can continue from where we last searched
1151 stream_scanner consumer = ^size_t(NSData *data) {
1152 __block size_t found_size = 0;
1153 __block size_t match_count = 0;
1154
1155 size_t size = data.length;
1156 const unsigned char *buffer = data.bytes;
1157 for (size_t i = 0; i < size; i++ ) {
1158 if (((const unsigned char *)buffer)[i] == ((const unsigned char *)by tes)[match_count]) {
1159 match_count += 1;
1160 if (match_count == length) {
1161 found_size = i + 1;
1162 break;
1163 }
1164 } else {
1165 match_count = 0;
1166 }
1167 }
1168 return found_size;
1169 };
1170 [self _addConsumerWithScanner:consumer callback:dataHandler];
1171 }
1172
1173
1174 // Returns true if did work
1175 - (BOOL)_innerPumpScanner {
1176
1177 BOOL didWork = NO;
1178
1179 if (self.readyState >= SR_CLOSING) {
1180 return didWork;
1181 }
1182
1183 if (!_consumers.count) {
1184 return didWork;
1185 }
1186
1187 size_t curSize = _readBuffer.length - _readBufferOffset;
1188 if (!curSize) {
1189 return didWork;
1190 }
1191
1192 SRIOConsumer *consumer = [_consumers objectAtIndex:0];
1193
1194 size_t bytesNeeded = consumer.bytesNeeded;
1195
1196 size_t foundSize = 0;
1197 if (consumer.consumer) {
1198 NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone: NO];
1199 foundSize = consumer.consumer(tempView);
1200 } else {
1201 assert(consumer.bytesNeeded);
1202 if (curSize >= bytesNeeded) {
1203 foundSize = bytesNeeded;
1204 } else if (consumer.readToCurrentFrame) {
1205 foundSize = curSize;
1206 }
1207 }
1208
1209 NSData *slice = nil;
1210 if (consumer.readToCurrentFrame || foundSize) {
1211 NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize);
1212 slice = [_readBuffer subdataWithRange:sliceRange];
1213
1214 _readBufferOffset += foundSize;
1215
1216 if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) {
1217 _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuff er.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0;
1218 }
1219
1220 if (consumer.unmaskBytes) {
1221 NSMutableData *mutableSlice = [slice mutableCopy];
1222
1223 NSUInteger len = mutableSlice.length;
1224 uint8_t *bytes = mutableSlice.mutableBytes;
1225
1226 for (NSUInteger i = 0; i < len; i++) {
1227 bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)];
1228 _currentReadMaskOffset += 1;
1229 }
1230
1231 slice = mutableSlice;
1232 }
1233
1234 if (consumer.readToCurrentFrame) {
1235 [_currentFrameData appendData:slice];
1236
1237 _readOpCount += 1;
1238
1239 if (_currentFrameOpcode == SROpCodeTextFrame) {
1240 // Validate UTF8 stuff.
1241 size_t currentDataSize = _currentFrameData.length;
1242 if (_currentFrameOpcode == SROpCodeTextFrame && currentDataSize > 0) {
1243 // TODO: Optimize the crap out of this. Don't really have t o copy all the data each time
1244
1245 size_t scanSize = currentDataSize - _currentStringScanPositi on;
1246
1247 NSData *scan_data = [_currentFrameData subdataWithRange:NSMa keRange(_currentStringScanPosition, scanSize)];
1248 int32_t valid_utf8_size = validate_dispatch_data_partial_str ing(scan_data);
1249
1250 if (valid_utf8_size == -1) {
1251 [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Tex t frames must be valid UTF-8"];
1252 dispatch_async(_workQueue, ^{
1253 [self _disconnect];
1254 });
1255 return didWork;
1256 } else {
1257 _currentStringScanPosition += valid_utf8_size;
1258 }
1259 }
1260
1261 }
1262
1263 consumer.bytesNeeded -= foundSize;
1264
1265 if (consumer.bytesNeeded == 0) {
1266 [_consumers removeObjectAtIndex:0];
1267 consumer.handler(self, nil);
1268 [_consumerPool returnConsumer:consumer];
1269 didWork = YES;
1270 }
1271 } else if (foundSize) {
1272 [_consumers removeObjectAtIndex:0];
1273 consumer.handler(self, slice);
1274 [_consumerPool returnConsumer:consumer];
1275 didWork = YES;
1276 }
1277 }
1278 return didWork;
1279 }
1280
1281 -(void)_pumpScanner;
1282 {
1283 [self assertOnWorkQueue];
1284
1285 if (!_isPumping) {
1286 _isPumping = YES;
1287 } else {
1288 return;
1289 }
1290
1291 while ([self _innerPumpScanner]) {
1292
1293 }
1294
1295 _isPumping = NO;
1296 }
1297
1298 //#define NOMASK
1299
1300 static const size_t SRFrameHeaderOverhead = 32;
1301
1302 - (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data;
1303 {
1304 [self assertOnWorkQueue];
1305
1306 if (nil == data) {
1307 return;
1308 }
1309
1310 NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSStrin g class]], @"NSString or NSData");
1311
1312 size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *) data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length];
1313
1314 NSMutableData *frame = [[NSMutableData alloc] initWithLength:payloadLength + SRFrameHeaderOverhead];
1315 if (!frame) {
1316 [self closeWithCode:SRStatusCodeMessageTooBig reason:@"Message too big"] ;
1317 return;
1318 }
1319 uint8_t *frame_buffer = (uint8_t *)[frame mutableBytes];
1320
1321 // set fin
1322 frame_buffer[0] = SRFinMask | opcode;
1323
1324 BOOL useMask = YES;
1325 #ifdef NOMASK
1326 useMask = NO;
1327 #endif
1328
1329 if (useMask) {
1330 // set the mask and header
1331 frame_buffer[1] |= SRMaskMask;
1332 }
1333
1334 size_t frame_buffer_size = 2;
1335
1336 const uint8_t *unmasked_payload = NULL;
1337 if ([data isKindOfClass:[NSData class]]) {
1338 unmasked_payload = (uint8_t *)[data bytes];
1339 } else if ([data isKindOfClass:[NSString class]]) {
1340 unmasked_payload = (const uint8_t *)[data UTF8String];
1341 } else {
1342 return;
1343 }
1344
1345 if (payloadLength < 126) {
1346 frame_buffer[1] |= payloadLength;
1347 } else if (payloadLength <= UINT16_MAX) {
1348 frame_buffer[1] |= 126;
1349 *((uint16_t *)(frame_buffer + frame_buffer_size)) = EndianU16_BtoN((uint 16_t)payloadLength);
1350 frame_buffer_size += sizeof(uint16_t);
1351 } else {
1352 frame_buffer[1] |= 127;
1353 *((uint64_t *)(frame_buffer + frame_buffer_size)) = EndianU64_BtoN((uint 64_t)payloadLength);
1354 frame_buffer_size += sizeof(uint64_t);
1355 }
1356
1357 if (!useMask) {
1358 for (size_t i = 0; i < payloadLength; i++) {
1359 frame_buffer[frame_buffer_size] = unmasked_payload[i];
1360 frame_buffer_size += 1;
1361 }
1362 } else {
1363 uint8_t *mask_key = frame_buffer + frame_buffer_size;
1364 SecRandomCopyBytes(kSecRandomDefault, sizeof(uint32_t), (uint8_t *)mask_ key);
1365 frame_buffer_size += sizeof(uint32_t);
1366
1367 // TODO: could probably optimize this with SIMD
1368 for (size_t i = 0; i < payloadLength; i++) {
1369 frame_buffer[frame_buffer_size] = unmasked_payload[i] ^ mask_key[i % sizeof(uint32_t)];
1370 frame_buffer_size += 1;
1371 }
1372 }
1373
1374 assert(frame_buffer_size <= [frame length]);
1375 frame.length = frame_buffer_size;
1376
1377 [self _writeData:frame];
1378 }
1379
1380 - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode;
1381 {
1382 if (_secure && !_pinnedCertFound && (eventCode == NSStreamEventHasBytesAvail able || eventCode == NSStreamEventHasSpaceAvailable)) {
1383
1384 NSArray *sslCerts = [_urlRequest SR_SSLPinnedCertificates];
1385 if (sslCerts) {
1386 SecTrustRef secTrust = (__bridge SecTrustRef)[aStream propertyForKey :(__bridge id)kCFStreamPropertySSLPeerTrust];
1387 if (secTrust) {
1388 NSInteger numCerts = SecTrustGetCertificateCount(secTrust);
1389 for (NSInteger i = 0; i < numCerts && !_pinnedCertFound; i++) {
1390 SecCertificateRef cert = SecTrustGetCertificateAtIndex(secTr ust, i);
1391 NSData *certData = CFBridgingRelease(SecCertificateCopyData( cert));
1392
1393 for (id ref in sslCerts) {
1394 SecCertificateRef trustedCert = (__bridge SecCertificate Ref)ref;
1395 NSData *trustedCertData = CFBridgingRelease(SecCertifica teCopyData(trustedCert));
1396
1397 if ([trustedCertData isEqualToData:certData]) {
1398 _pinnedCertFound = YES;
1399 break;
1400 }
1401 }
1402 }
1403 }
1404
1405 if (!_pinnedCertFound) {
1406 dispatch_async(_workQueue, ^{
1407 [self _failWithError:[NSError errorWithDomain:SRWebSocketErr orDomain code:23556 userInfo:[NSDictionary dictionaryWithObject:[NSString string WithFormat:@"Invalid server cert"] forKey:NSLocalizedDescriptionKey]]];
1408 });
1409 return;
1410 }
1411 }
1412 }
1413
1414 dispatch_async(_workQueue, ^{
1415 switch (eventCode) {
1416 case NSStreamEventOpenCompleted: {
1417 SRFastLog(@"NSStreamEventOpenCompleted %@", aStream);
1418 if (self.readyState >= SR_CLOSING) {
1419 return;
1420 }
1421 assert(_readBuffer);
1422
1423 if (self.readyState == SR_CONNECTING && aStream == _inputStream) {
1424 [self didConnect];
1425 }
1426 [self _pumpWriting];
1427 [self _pumpScanner];
1428 break;
1429 }
1430
1431 case NSStreamEventErrorOccurred: {
1432 SRFastLog(@"NSStreamEventErrorOccurred %@ %@", aStream, [[aStrea m streamError] copy]);
1433 /// TODO specify error better!
1434 [self _failWithError:aStream.streamError];
1435 _readBufferOffset = 0;
1436 [_readBuffer setLength:0];
1437 break;
1438
1439 }
1440
1441 case NSStreamEventEndEncountered: {
1442 [self _pumpScanner];
1443 SRFastLog(@"NSStreamEventEndEncountered %@", aStream);
1444 if (aStream.streamError) {
1445 [self _failWithError:aStream.streamError];
1446 } else {
1447 if (self.readyState != SR_CLOSED) {
1448 self.readyState = SR_CLOSED;
1449 _selfRetain = nil;
1450 }
1451
1452 if (!_sentClose && !_failed) {
1453 _sentClose = YES;
1454 // If we get closed in this state it's probably not clea n because we should be sending this when we send messages
1455 [self _performDelegateBlock:^{
1456 if ([self.delegate respondsToSelector:@selector(webS ocket:didCloseWithCode:reason:wasClean:)]) {
1457 [self.delegate webSocket:self didCloseWithCode:S RStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO];
1458 }
1459 }];
1460 }
1461 }
1462
1463 break;
1464 }
1465
1466 case NSStreamEventHasBytesAvailable: {
1467 SRFastLog(@"NSStreamEventHasBytesAvailable %@", aStream);
1468 const int bufferSize = 2048;
1469 uint8_t buffer[bufferSize];
1470
1471 while (_inputStream.hasBytesAvailable) {
1472 NSInteger bytes_read = [_inputStream read:buffer maxLength:b ufferSize];
1473
1474 if (bytes_read > 0) {
1475 [_readBuffer appendBytes:buffer length:bytes_read];
1476 } else if (bytes_read < 0) {
1477 [self _failWithError:_inputStream.streamError];
1478 }
1479
1480 if (bytes_read != bufferSize) {
1481 break;
1482 }
1483 };
1484 [self _pumpScanner];
1485 break;
1486 }
1487
1488 case NSStreamEventHasSpaceAvailable: {
1489 SRFastLog(@"NSStreamEventHasSpaceAvailable %@", aStream);
1490 [self _pumpWriting];
1491 break;
1492 }
1493
1494 default:
1495 SRFastLog(@"(default) %@", aStream);
1496 break;
1497 }
1498 });
1499 }
1500
1501 @end
1502
1503
1504 @implementation SRIOConsumer
1505
1506 @synthesize bytesNeeded = _bytesNeeded;
1507 @synthesize consumer = _scanner;
1508 @synthesize handler = _handler;
1509 @synthesize readToCurrentFrame = _readToCurrentFrame;
1510 @synthesize unmaskBytes = _unmaskBytes;
1511
1512 - (void)setupWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unma skBytes:(BOOL)unmaskBytes;
1513 {
1514 _scanner = [scanner copy];
1515 _handler = [handler copy];
1516 _bytesNeeded = bytesNeeded;
1517 _readToCurrentFrame = readToCurrentFrame;
1518 _unmaskBytes = unmaskBytes;
1519 assert(_scanner || _bytesNeeded);
1520 }
1521
1522
1523 @end
1524
1525
1526 @implementation SRIOConsumerPool {
1527 NSUInteger _poolSize;
1528 NSMutableArray *_bufferedConsumers;
1529 }
1530
1531 - (id)initWithBufferCapacity:(NSUInteger)poolSize;
1532 {
1533 self = [super init];
1534 if (self) {
1535 _poolSize = poolSize;
1536 _bufferedConsumers = [[NSMutableArray alloc] initWithCapacity:poolSize];
1537 }
1538 return self;
1539 }
1540
1541 - (id)init
1542 {
1543 return [self initWithBufferCapacity:8];
1544 }
1545
1546 - (SRIOConsumer *)consumerWithScanner:(stream_scanner)scanner handler:(data_call back)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurr entFrame unmaskBytes:(BOOL)unmaskBytes;
1547 {
1548 SRIOConsumer *consumer = nil;
1549 if (_bufferedConsumers.count) {
1550 consumer = [_bufferedConsumers lastObject];
1551 [_bufferedConsumers removeLastObject];
1552 } else {
1553 consumer = [[SRIOConsumer alloc] init];
1554 }
1555
1556 [consumer setupWithScanner:scanner handler:handler bytesNeeded:bytesNeeded r eadToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes];
1557
1558 return consumer;
1559 }
1560
1561 - (void)returnConsumer:(SRIOConsumer *)consumer;
1562 {
1563 if (_bufferedConsumers.count < _poolSize) {
1564 [_bufferedConsumers addObject:consumer];
1565 }
1566 }
1567
1568 @end
1569
1570
1571 @implementation NSURLRequest (CertificateAdditions)
1572
1573 - (NSArray *)SR_SSLPinnedCertificates;
1574 {
1575 return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:s elf];
1576 }
1577
1578 @end
1579
1580 @implementation NSMutableURLRequest (CertificateAdditions)
1581
1582 - (NSArray *)SR_SSLPinnedCertificates;
1583 {
1584 return [NSURLProtocol propertyForKey:@"SR_SSLPinnedCertificates" inRequest:s elf];
1585 }
1586
1587 - (void)setSR_SSLPinnedCertificates:(NSArray *)SR_SSLPinnedCertificates;
1588 {
1589 [NSURLProtocol setProperty:SR_SSLPinnedCertificates forKey:@"SR_SSLPinnedCer tificates" inRequest:self];
1590 }
1591
1592 @end
1593
1594 @implementation NSURL (SRWebSocket)
1595
1596 - (NSString *)SR_origin;
1597 {
1598 NSString *scheme = [self.scheme lowercaseString];
1599
1600 if ([scheme isEqualToString:@"wss"]) {
1601 scheme = @"https";
1602 } else if ([scheme isEqualToString:@"ws"]) {
1603 scheme = @"http";
1604 }
1605
1606 if (self.port) {
1607 return [NSString stringWithFormat:@"%@://%@:%@/", scheme, self.host, sel f.port];
1608 } else {
1609 return [NSString stringWithFormat:@"%@://%@/", scheme, self.host];
1610 }
1611 }
1612
1613 @end
1614
1615 //#define SR_ENABLE_LOG
1616
1617 static inline void SRFastLog(NSString *format, ...) {
1618 #ifdef SR_ENABLE_LOG
1619 __block va_list arg_list;
1620 va_start (arg_list, format);
1621
1622 NSString *formattedString = [[NSString alloc] initWithFormat:format argument s:arg_list];
1623
1624 va_end(arg_list);
1625
1626 NSLog(@"[SR] %@", formattedString);
1627 #endif
1628 }
1629
1630
1631 #ifdef HAS_ICU
1632
1633 static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
1634 if ([data length] > INT32_MAX) {
1635 // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere.
1636 return -1;
1637 }
1638
1639 int32_t size = (int32_t)[data length];
1640
1641 const void * contents = [data bytes];
1642 const uint8_t *str = (const uint8_t *)contents;
1643
1644 UChar32 codepoint = 1;
1645 int32_t offset = 0;
1646 int32_t lastOffset = 0;
1647 while(offset < size && codepoint > 0) {
1648 lastOffset = offset;
1649 U8_NEXT(str, offset, size, codepoint);
1650 }
1651
1652 if (codepoint == -1) {
1653 // Check to see if the last byte is valid or whether it was just continu ing
1654 if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset] ) + lastOffset < (int32_t)size) {
1655
1656 size = -1;
1657 } else {
1658 uint8_t leadByte = str[lastOffset];
1659 U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte));
1660
1661 for (int i = lastOffset + 1; i < offset; i++) {
1662 if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(s tr[i])) {
1663 size = -1;
1664 }
1665 }
1666
1667 if (size != -1) {
1668 size = lastOffset;
1669 }
1670 }
1671 }
1672
1673 if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes ] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) {
1674 size = -1;
1675 }
1676
1677 return size;
1678 }
1679
1680 #else
1681
1682 // This is a hack, and probably not optimal
1683 static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
1684 static const int maxCodepointSize = 3;
1685
1686 for (int i = 0; i < maxCodepointSize; i++) {
1687 NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO];
1688 if (str) {
1689 return data.length - i;
1690 }
1691 }
1692
1693 return -1;
1694 }
1695
1696 #endif
1697
1698 static _SRRunLoopThread *networkThread = nil;
1699 static NSRunLoop *networkRunLoop = nil;
1700
1701 @implementation NSRunLoop (SRWebSocket)
1702
1703 + (NSRunLoop *)SR_networkRunLoop {
1704 static dispatch_once_t onceToken;
1705 dispatch_once(&onceToken, ^{
1706 networkThread = [[_SRRunLoopThread alloc] init];
1707 networkThread.name = @"com.squareup.SocketRocket.NetworkThread";
1708 [networkThread start];
1709 networkRunLoop = networkThread.runLoop;
1710 });
1711
1712 return networkRunLoop;
1713 }
1714
1715 @end
1716
1717
1718 @implementation _SRRunLoopThread {
1719 dispatch_group_t _waitGroup;
1720 }
1721
1722 @synthesize runLoop = _runLoop;
1723
1724 - (void)dealloc
1725 {
1726 sr_dispatch_release(_waitGroup);
1727 }
1728
1729 - (id)init
1730 {
1731 self = [super init];
1732 if (self) {
1733 _waitGroup = dispatch_group_create();
1734 dispatch_group_enter(_waitGroup);
1735 }
1736 return self;
1737 }
1738
1739 - (void)main;
1740 {
1741 @autoreleasepool {
1742 _runLoop = [NSRunLoop currentRunLoop];
1743 dispatch_group_leave(_waitGroup);
1744
1745 NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate distantFuture ] interval:0.0 target:nil selector:nil userInfo:nil repeats:NO];
1746 [_runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
1747
1748 while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distant Future]]) {
1749
1750 }
1751 assert(NO);
1752 }
1753 }
1754
1755 - (NSRunLoop *)runLoop;
1756 {
1757 dispatch_group_wait(_waitGroup, DISPATCH_TIME_FOREVER);
1758 return _runLoop;
1759 }
1760
1761 @end
OLDNEW
« no previous file with comments | « talk/examples/objc/AppRTCDemo/third_party/SocketRocket/SRWebSocket.h ('k') | talk/examples/objc/Icon.png » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698