| OLD | NEW |
| (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 | |
| OLD | NEW |