Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial Version

  • Loading branch information...
commit d0ce5ee63ef521e658e540cbba00b924fd2388a2 0 parents
@mikelikespie mikelikespie authored
2  .gitignore
@@ -0,0 +1,2 @@
+.idea/
+.env/
15 LICENSE
@@ -0,0 +1,15 @@
+
+ Copyright 2012 Square Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
23 SRTests/SRTAppDelegate.h
@@ -0,0 +1,23 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface SRTAppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
209 SRTests/SRTAppDelegate.m
@@ -0,0 +1,209 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SRTAppDelegate.h"
+#import "SRWebSocket.h"
+
+#define SRLogDebug(format, ...) //NSLog(format, __VA_ARGS__)
+
+@interface TestOperation : NSOperation <SRWebSocketDelegate>
+
+- (id)initWithTestNumber:(NSInteger)testNumber;
+
+@end
+
+
+@interface SRTAppDelegate () <SRWebSocketDelegate>
+
+@end
+
+
+@implementation SRTAppDelegate {
+ SRWebSocket *_curWebSocket;
+ NSInteger _testCount;
+ NSInteger _curTest;
+ NSMutableArray *_sockets;
+}
+
+@synthesize window = _window;
+
+//- (void)_getTests
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
+ // Override point for customization after application launch.
+ self.window.backgroundColor = [UIColor whiteColor];
+ [self.window makeKeyAndVisible];
+
+ _sockets = [[NSMutableArray alloc] init];
+
+#if 1
+ __unsafe_unretained SRTAppDelegate *weakself = self;
+ _curWebSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"wss://mcnugget.local:443/getCaseCount"]]];
+ _curWebSocket.onMessage = ^(SRWebSocket *webSocket, NSString *message) {
+ NSOperation *finishOperation = [NSBlockOperation blockOperationWithBlock:^{
+ weakself->_curWebSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"wss://mcnugget.local:443/updateReports?agent=socketrocket1"]]];
+
+ NSLog(@"-- Updating Reports");
+ weakself->_curWebSocket.onClose = ^(SRWebSocket *webSocket, NSInteger code, NSString *reason, BOOL wasClean) {
+ NSLog(@"-- reports updated... exiting");
+ };
+ weakself->_curWebSocket.onError = ^(SRWebSocket *webSocket, NSError *error) {
+ NSLog(@"Error updating reports %@", error.localizedDescription);
+ };
+
+ [weakself->_curWebSocket open];
+ }];
+
+
+ NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
+ testQueue.maxConcurrentOperationCount = 1;
+
+ for (int i = 0; i < [message integerValue]; i++) {
+ NSOperation *op = [[TestOperation alloc] initWithTestNumber:i + 1];
+ [finishOperation addDependency:op];
+ [testQueue addOperation:op];
+ }
+
+ [testQueue addOperation:finishOperation];
+ };
+
+ [_curWebSocket open];
+
+#else
+ double delayInSeconds = 0.1;
+ dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
+ dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
+ _listener = [[SRWebSocketListener alloc] init];
+
+ _listener.onMessage = ^(SRWebSocket *socket, id message) {
+ [socket send:message];
+
+#if 0
+ if ([message isKindOfClass:[NSString class]]) {
+ NSLog(@"Echoing String for %@", [message substringToIndex:MIN(128, [message length])]);
+ } else {
+ NSLog(@"Echoing String for %@", [message subdataWithRange:NSMakeRange(0, MIN(128, [message length]))]);
+ }
+#endif
+
+ };
+ _listener.onClose = ^(SRWebSocket *webSocket, NSInteger code, NSString *reason, BOOL wasClean) {
+ NSLog(@"closing");
+ };
+ _listener.onError = ^(SRWebSocket *webSocket, NSError *error) {
+ NSLog(@"error %@", error.localizedDescription);
+ };
+
+ [_listener startListeningOnPort:9000];
+
+ });
+
+#endif
+
+ return YES;
+}
+
+@end
+
+@interface TestOperation ()
+
+@property (nonatomic) BOOL isFinished;
+@property (nonatomic) BOOL isExecuting;
+
+@end
+
+@implementation TestOperation {
+ NSInteger _testNumber;
+ SRWebSocket *_webSocket;
+}
+
+@synthesize isFinished = _isFinished;
+@synthesize isExecuting = _isExecuting;
+
+- (id)initWithTestNumber:(NSInteger)testNumber;
+{
+ self = [super init];
+ if (self) {
+ _testNumber = testNumber;
+ _isExecuting = NO;
+ _isFinished = NO;
+ }
+ return self;
+}
+
+- (BOOL)isConcurrent;
+{
+ return YES;
+}
+
+- (void)start;
+{
+ NSLog(@"Starting test %d", _testNumber);
+ self.isExecuting = YES;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ _webSocket = [[SRWebSocket alloc] initWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"ws://localhost:9001/runCase?case=%d&agent=socketrocket1", _testNumber]]]];
+ _webSocket.delegate = self;
+ [_webSocket open];
+ });
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
+{
+ NSLog(@"Received close for %d (%d, %@)", _testNumber, code, reason);
+
+ [self willChangeValueForKey:@"isExecuting"];
+ [self willChangeValueForKey:@"isFinished"];
+ _isFinished = YES;
+ _isExecuting = NO;
+ _webSocket = nil;
+ [self didChangeValueForKey:@"isExecuting"];
+ [self didChangeValueForKey:@"isFinished"];
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSString *)message;
+{
+ if ([message isKindOfClass:[NSString class]]) {
+ SRLogDebug(@"Echoing String for %d %@", _testNumber, [message substringToIndex:MIN(128, [message length])]);
+ } else {
+ SRLogDebug(@"Echoing String for %d %@", _testNumber, [message subd:MIN(128, [message length])]);
+ }
+ [webSocket send:message];
+
+ double delayInSeconds = 30.0;
+ dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
+ dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
+ if (!self.isFinished) {
+ NSLog(@"Timing Out");
+ [_webSocket closeWithCode:0 reason:nil];
+ }
+ });
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
+{
+ NSLog(@"failed with error %@", [error localizedDescription]);
+ [self willChangeValueForKey:@"isExecuting"];
+ [self willChangeValueForKey:@"isFinished"];
+ _isFinished = YES;
+ _isExecuting = NO;
+ _webSocket = nil;
+ [self didChangeValueForKey:@"isExecuting"];
+ [self didChangeValueForKey:@"isFinished"];
+}
+
+@end
47 SRTests/SRTests-Info.plist
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleDisplayName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIconFiles</key>
+ <array/>
+ <key>CFBundleIdentifier</key>
+ <string>org.lolrus.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>${PRODUCT_NAME}</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array>
+ <string>armv7</string>
+ </array>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+</dict>
+</plist>
35 SRTests/SRTests-Prefix.pch
@@ -0,0 +1,35 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Availability.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __IPHONE_3_0
+#warning "This project uses features only available in iOS SDK 3.0 and later."
+#endif
+
+#ifdef __OBJC__
+ #import <UIKit/UIKit.h>
+ #import <Foundation/Foundation.h>
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
2  SRTests/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
26 SRTests/main.m
@@ -0,0 +1,26 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <UIKit/UIKit.h>
+
+#import "SRTAppDelegate.h"
+
+int main(int argc, char *argv[])
+{
+ @autoreleasepool {
+ return UIApplicationMain(argc, argv, nil, NSStringFromClass([SRTAppDelegate class]));
+ }
+}
176 SRWebSocketTests/SRTAutobahnTests.m
@@ -0,0 +1,176 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <SenTestingKit/SenTestingKit.h>
+#import "SRWebSocket.h"
+
+#define SRLogDebug(format, ...)
+//#define SRLogDebug(format, ...) NSLog(format, __VA_ARGS__)
+
+@interface SRTAutobahnTests : SenTestCase
+@end
+
+@interface TestOperation : NSOperation <SRWebSocketDelegate>
+
+- (id)initWithTestNumber:(NSInteger)testNumber;
+
+@end
+
+@implementation SRTAutobahnTests {
+ SRWebSocket *_curWebSocket;
+ NSInteger _testCount;
+ NSInteger _curTest;
+ NSMutableArray *_sockets;
+}
+
+- (void)testFuzzer;
+{
+ _sockets = [[NSMutableArray alloc] init];
+
+ NSOperationQueue *testQueue = [[NSOperationQueue alloc] init];
+
+ __block BOOL hasFinished = NO;
+ __block BOOL hasFailed = NO;
+
+ __weak SRTAutobahnTests *weakself = self;
+ _curWebSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:@"ws://localhost:9001/getCaseCount"]];
+ _curWebSocket.onMessage = ^(SRWebSocket *webSocket, NSString *message) {
+ NSOperation *finishOperation = [NSBlockOperation blockOperationWithBlock:^{
+ weakself->_curWebSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:@"ws://localhost:9001/updateReports?agent=socketrocket"]];
+
+ NSLog(@"-- Updating Reports");
+ weakself->_curWebSocket.onClose = ^(SRWebSocket *webSocket, NSInteger code, NSString *reason, BOOL wasClean) {
+ NSLog(@"-- reports updated... exiting");
+ hasFinished = YES;
+ };
+ weakself->_curWebSocket.onError = ^(SRWebSocket *webSocket, NSError *error) {
+ NSLog(@"Error updating reports %@", error.localizedDescription);
+ hasFailed = YES;
+ hasFinished = YES;
+ };
+
+ [weakself->_curWebSocket open];
+ }];
+
+
+ testQueue.maxConcurrentOperationCount = 1;
+
+ for (int i = 0; i < [message integerValue]; i++) {
+ NSOperation *op = [[TestOperation alloc] initWithTestNumber:i + 1];
+ [finishOperation addDependency:op];
+ [testQueue addOperation:op];
+ }
+
+ [testQueue addOperation:finishOperation];
+ };
+
+ [_curWebSocket open];
+ [self runCurrentRunLoopUntilTestPasses:^BOOL{
+ return hasFinished;
+ } timeout:60 * 60];
+
+ STAssertFalse(hasFailed, @"timeout");
+}
+
+@end
+
+@interface TestOperation ()
+
+@property (nonatomic) BOOL isFinished;
+@property (nonatomic) BOOL isExecuting;
+
+@end
+
+@implementation TestOperation {
+ NSInteger _testNumber;
+ SRWebSocket *_webSocket;
+}
+
+@synthesize isFinished = _isFinished;
+@synthesize isExecuting = _isExecuting;
+
+- (id)initWithTestNumber:(NSInteger)testNumber;
+{
+ self = [super init];
+ if (self) {
+ _testNumber = testNumber;
+ _isExecuting = NO;
+ _isFinished = NO;
+ }
+ return self;
+}
+
+- (BOOL)isConcurrent;
+{
+ return YES;
+}
+
+- (void)start;
+{
+ NSLog(@"Starting test %d", _testNumber);
+ self.isExecuting = YES;
+ dispatch_async(dispatch_get_main_queue(), ^{
+ _webSocket = [[SRWebSocket alloc] initWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"ws://localhost:9001/runCase?case=%d&agent=socketrocket", _testNumber]]];
+ _webSocket.delegate = self;
+ [_webSocket open];
+ });
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
+{
+ NSLog(@"Received close for %d", _testNumber);
+
+ [self willChangeValueForKey:@"isExecuting"];
+ [self willChangeValueForKey:@"isFinished"];
+ _isFinished = YES;
+ _isExecuting = NO;
+ _webSocket = nil;
+ [self didChangeValueForKey:@"isExecuting"];
+ [self didChangeValueForKey:@"isFinished"];
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message;
+{
+ if ([message isKindOfClass:[NSString class]]) {
+ SRLogDebug(@"Echoing String for %d %@", _testNumber, [(NSString *)message substringToIndex:MIN(128, [message length])]);
+ } else {
+ SRLogDebug(@"Echoing String for %d %@", _testNumber, [(NSData *)message subdataWithRange:NSMakeRange(0, MIN(128, ([message length])))]);
+ }
+ [webSocket send:message];
+
+ double delayInSeconds = 100.0;
+ dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
+ dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
+ if (!self.isFinished) {
+ NSLog(@"Timing Out");
+ [_webSocket closeWithCode:0 reason:nil];
+ }
+ });
+}
+
+- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
+{
+ NSLog(@"failed with error %@", [error localizedDescription]);
+ [self willChangeValueForKey:@"isExecuting"];
+ [self willChangeValueForKey:@"isFinished"];
+ _isFinished = YES;
+ _isExecuting = NO;
+ _webSocket = nil;
+ [self didChangeValueForKey:@"isExecuting"];
+ [self didChangeValueForKey:@"isFinished"];
+}
+
+@end
22 SRWebSocketTests/SRWebSocketTests-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>${EXECUTABLE_NAME}</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.lolrus.${PRODUCT_NAME:rfc1034identifier}</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>BNDL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+</dict>
+</plist>
22 SRWebSocketTests/SRWebSocketTests-Prefix.pch
@@ -0,0 +1,22 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifdef __OBJC__
+ #import <UIKit/UIKit.h>
+ #import <Foundation/Foundation.h>
+ #import <SenTestingKit/SenTestingKit.h>
+ #import "SenTestCase+SRTAdditions.h"
+#endif
27 SRWebSocketTests/SenTestCase+SRTAdditions.h
@@ -0,0 +1,27 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <SenTestingKit/SenTestingKit.h>
+
+
+typedef BOOL (^PXPredicateBlock)();
+
+
+@interface SenTestCase (PXAdditions)
+
+- (void)runCurrentRunLoopUntilTestPasses:(PXPredicateBlock)predicate timeout:(NSTimeInterval)timeout;
+
+@end
38 SRWebSocketTests/SenTestCase+SRTAdditions.m
@@ -0,0 +1,38 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "SenTestCase+SRTAdditions.h"
+
+
+@implementation SenTestCase (SRTAdditions)
+
+- (void)runCurrentRunLoopUntilTestPasses:(PXPredicateBlock)predicate timeout:(NSTimeInterval)timeout;
+{
+ NSDate *timeoutDate = [NSDate dateWithTimeIntervalSinceNow:timeout];
+
+ NSTimeInterval timeoutTime = [timeoutDate timeIntervalSinceReferenceDate];
+ NSTimeInterval currentTime;
+
+ for (currentTime = [NSDate timeIntervalSinceReferenceDate];
+ !predicate() && currentTime < timeoutTime;
+ currentTime = [NSDate timeIntervalSinceReferenceDate]) {
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
+ }
+
+ STAssertTrue(currentTime <= timeoutTime, @"Timed out");
+}
+
+@end
2  SRWebSocketTests/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
8 SRWebSocketTests/foo.mm
@@ -0,0 +1,8 @@
+//
+// foo.m
+// SocketRocket
+//
+// Created by Mike Lewis on 10/31/11.
+// Copyright (c) 2011 __MyCompanyName__. All rights reserved.
+//
+
613 SocketRocket.xcodeproj/project.pbxproj
@@ -0,0 +1,613 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ F6016C7C146124B20037BB3D /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = F6016C7B146124B20037BB3D /* base64.c */; };
+ F6016C7F146124ED0037BB3D /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = F6016C7E146124ED0037BB3D /* base64.h */; };
+ F6016C8714620EC60037BB3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
+ F6016C8814620EC70037BB3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
+ F6016C8914620ECC0037BB3D /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
+ F6016C8A1462143C0037BB3D /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
+ F6572126146C7B6A00D6B8A9 /* NSData+SRB64Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */; };
+ F66996FE146759FE0014B93E /* libSocketRocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B2082D1450F597009315AF /* libSocketRocket.a */; };
+ F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; };
+ F6A12CD2145119B700C1D980 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; };
+ F6A12CD61451231B00C1D980 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
+ F6AE451D145906A70022AF3C /* libSocketRocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B2082D1450F597009315AF /* libSocketRocket.a */; };
+ F6AE4520145906B20022AF3C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
+ F6AE4521145906B20022AF3C /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208431450F611009315AF /* UIKit.framework */; };
+ F6AE4522145906B20022AF3C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208461450F611009315AF /* CoreGraphics.framework */; };
+ F6AE4523145906C10022AF3C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208461450F611009315AF /* CoreGraphics.framework */; };
+ F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
+ F6AE4528145907D30022AF3C /* SenTestCase+SRTAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */; };
+ F6B208441450F611009315AF /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208431450F611009315AF /* UIKit.framework */; };
+ F6B208451450F611009315AF /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
+ F6B208471450F611009315AF /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208461450F611009315AF /* CoreGraphics.framework */; };
+ F6B2084D1450F611009315AF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F6B2084B1450F611009315AF /* InfoPlist.strings */; };
+ F6B2084F1450F611009315AF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F6B2084E1450F611009315AF /* main.m */; };
+ F6B208531450F611009315AF /* SRTAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = F6B208521450F611009315AF /* SRTAppDelegate.m */; };
+ F6BDA804145900D200FE3253 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6BDA803145900D200FE3253 /* SenTestingKit.framework */; };
+ F6BDA805145900D200FE3253 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208431450F611009315AF /* UIKit.framework */; };
+ F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
+ F6BDA80C145900D200FE3253 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F6BDA80A145900D200FE3253 /* InfoPlist.strings */; };
+ F6BDA8161459016900FE3253 /* SRTAutobahnTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */; };
+ F6C41C96145F7C4700641356 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
+ F6C41C98145F7C6100641356 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
+ F6C41C99145F7C7700641356 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ F6016C7B146124B20037BB3D /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = base64.c; sourceTree = "<group>"; };
+ F6016C7E146124ED0037BB3D /* base64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = base64.h; sourceTree = "<group>"; };
+ F6572123146C7B6A00D6B8A9 /* NSData+SRB64Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+SRB64Additions.h"; sourceTree = "<group>"; };
+ F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+SRB64Additions.m"; sourceTree = "<group>"; };
+ F6A12CCF145119B700C1D980 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = "<group>"; };
+ F6A12CD0145119B700C1D980 /* SRWebSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRWebSocket.m; sourceTree = "<group>"; };
+ F6A12CD3145122FC00C1D980 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
+ F6A12CD51451231B00C1D980 /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
+ F6AE4526145907D30022AF3C /* SenTestCase+SRTAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SenTestCase+SRTAdditions.h"; sourceTree = "<group>"; };
+ F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SenTestCase+SRTAdditions.m"; sourceTree = "<group>"; };
+ F6B2082D1450F597009315AF /* libSocketRocket.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libSocketRocket.a; sourceTree = BUILT_PRODUCTS_DIR; };
+ F6B208301450F597009315AF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+ F6B208341450F597009315AF /* SocketRocket-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SocketRocket-Prefix.pch"; sourceTree = "<group>"; };
+ F6B208411450F611009315AF /* SRTests.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SRTests.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ F6B208431450F611009315AF /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+ F6B208461450F611009315AF /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
+ F6B2084A1450F611009315AF /* SRTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SRTests-Info.plist"; sourceTree = "<group>"; };
+ F6B2084C1450F611009315AF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ F6B2084E1450F611009315AF /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ F6B208501450F611009315AF /* SRTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SRTests-Prefix.pch"; sourceTree = "<group>"; };
+ F6B208511450F611009315AF /* SRTAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SRTAppDelegate.h; sourceTree = "<group>"; };
+ F6B208521450F611009315AF /* SRTAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SRTAppDelegate.m; sourceTree = "<group>"; };
+ F6BDA802145900D200FE3253 /* SRWebSocketTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SRWebSocketTests.octest; sourceTree = BUILT_PRODUCTS_DIR; };
+ F6BDA803145900D200FE3253 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; };
+ F6BDA809145900D200FE3253 /* SRWebSocketTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SRWebSocketTests-Info.plist"; sourceTree = "<group>"; };
+ F6BDA80B145900D200FE3253 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ F6BDA810145900D200FE3253 /* SRWebSocketTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SRWebSocketTests-Prefix.pch"; sourceTree = "<group>"; };
+ F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRTAutobahnTests.m; sourceTree = "<group>"; };
+ F6C41C95145F7C4700641356 /* libicucore.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libicucore.dylib; path = usr/lib/libicucore.dylib; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ F6B2082A1450F597009315AF /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6C41C96145F7C4700641356 /* libicucore.dylib in Frameworks */,
+ F6AE4520145906B20022AF3C /* Foundation.framework in Frameworks */,
+ F6AE4521145906B20022AF3C /* UIKit.framework in Frameworks */,
+ F6AE4522145906B20022AF3C /* CoreGraphics.framework in Frameworks */,
+ F6016C8914620ECC0037BB3D /* Security.framework in Frameworks */,
+ F6016C8A1462143C0037BB3D /* CFNetwork.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F6B2083E1450F611009315AF /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6C41C99145F7C7700641356 /* libicucore.dylib in Frameworks */,
+ F6A12CD61451231B00C1D980 /* CFNetwork.framework in Frameworks */,
+ F6B208441450F611009315AF /* UIKit.framework in Frameworks */,
+ F6B208451450F611009315AF /* Foundation.framework in Frameworks */,
+ F6B208471450F611009315AF /* CoreGraphics.framework in Frameworks */,
+ F6016C8714620EC60037BB3D /* Security.framework in Frameworks */,
+ F66996FE146759FE0014B93E /* libSocketRocket.a in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F6BDA7FE145900D200FE3253 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6C41C98145F7C6100641356 /* libicucore.dylib in Frameworks */,
+ F6BDA804145900D200FE3253 /* SenTestingKit.framework in Frameworks */,
+ F6BDA805145900D200FE3253 /* UIKit.framework in Frameworks */,
+ F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */,
+ F6AE451D145906A70022AF3C /* libSocketRocket.a in Frameworks */,
+ F6AE4523145906C10022AF3C /* CoreGraphics.framework in Frameworks */,
+ F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */,
+ F6016C8814620EC70037BB3D /* Security.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ F6B208221450F597009315AF = {
+ isa = PBXGroup;
+ children = (
+ F6B208321450F597009315AF /* SocketRocket */,
+ F6B208481450F611009315AF /* SRTests */,
+ F6BDA807145900D200FE3253 /* SRWebSocketTests */,
+ F6B2082F1450F597009315AF /* Frameworks */,
+ F6B2082E1450F597009315AF /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ F6B2082E1450F597009315AF /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ F6B2082D1450F597009315AF /* libSocketRocket.a */,
+ F6B208411450F611009315AF /* SRTests.app */,
+ F6BDA802145900D200FE3253 /* SRWebSocketTests.octest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ F6B2082F1450F597009315AF /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ F6C41C95145F7C4700641356 /* libicucore.dylib */,
+ F6A12CD51451231B00C1D980 /* CFNetwork.framework */,
+ F6A12CD3145122FC00C1D980 /* Security.framework */,
+ F6B208301450F597009315AF /* Foundation.framework */,
+ F6B208431450F611009315AF /* UIKit.framework */,
+ F6B208461450F611009315AF /* CoreGraphics.framework */,
+ F6BDA803145900D200FE3253 /* SenTestingKit.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "<group>";
+ };
+ F6B208321450F597009315AF /* SocketRocket */ = {
+ isa = PBXGroup;
+ children = (
+ F6016C7B146124B20037BB3D /* base64.c */,
+ F6016C7E146124ED0037BB3D /* base64.h */,
+ F6A12CCF145119B700C1D980 /* SRWebSocket.h */,
+ F6A12CD0145119B700C1D980 /* SRWebSocket.m */,
+ F6B208331450F597009315AF /* Supporting Files */,
+ F6572123146C7B6A00D6B8A9 /* NSData+SRB64Additions.h */,
+ F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */,
+ );
+ path = SocketRocket;
+ sourceTree = "<group>";
+ };
+ F6B208331450F597009315AF /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ F6B208341450F597009315AF /* SocketRocket-Prefix.pch */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ F6B208481450F611009315AF /* SRTests */ = {
+ isa = PBXGroup;
+ children = (
+ F6B208511450F611009315AF /* SRTAppDelegate.h */,
+ F6B208521450F611009315AF /* SRTAppDelegate.m */,
+ F6B208491450F611009315AF /* Supporting Files */,
+ );
+ path = SRTests;
+ sourceTree = "<group>";
+ };
+ F6B208491450F611009315AF /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ F6B2084A1450F611009315AF /* SRTests-Info.plist */,
+ F6B2084B1450F611009315AF /* InfoPlist.strings */,
+ F6B2084E1450F611009315AF /* main.m */,
+ F6B208501450F611009315AF /* SRTests-Prefix.pch */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+ F6BDA807145900D200FE3253 /* SRWebSocketTests */ = {
+ isa = PBXGroup;
+ children = (
+ F6BDA808145900D200FE3253 /* Supporting Files */,
+ F6BDA810145900D200FE3253 /* SRWebSocketTests-Prefix.pch */,
+ F6AE4526145907D30022AF3C /* SenTestCase+SRTAdditions.h */,
+ F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */,
+ F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */,
+ );
+ path = SRWebSocketTests;
+ sourceTree = "<group>";
+ };
+ F6BDA808145900D200FE3253 /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ F6BDA809145900D200FE3253 /* SRWebSocketTests-Info.plist */,
+ F6BDA80A145900D200FE3253 /* InfoPlist.strings */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ F6B2082B1450F597009315AF /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */,
+ F6016C7F146124ED0037BB3D /* base64.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ F6B2082C1450F597009315AF /* SocketRocket */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F6B2083A1450F597009315AF /* Build configuration list for PBXNativeTarget "SocketRocket" */;
+ buildPhases = (
+ F6B208291450F597009315AF /* Sources */,
+ F6B2082A1450F597009315AF /* Frameworks */,
+ F6B2082B1450F597009315AF /* Headers */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = SocketRocket;
+ productName = SocketRocket;
+ productReference = F6B2082D1450F597009315AF /* libSocketRocket.a */;
+ productType = "com.apple.product-type.library.static";
+ };
+ F6B208401450F611009315AF /* SRTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F6B208541450F611009315AF /* Build configuration list for PBXNativeTarget "SRTests" */;
+ buildPhases = (
+ F6B2083D1450F611009315AF /* Sources */,
+ F6B2083E1450F611009315AF /* Frameworks */,
+ F6B2083F1450F611009315AF /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = SRTests;
+ productName = SRTests;
+ productReference = F6B208411450F611009315AF /* SRTests.app */;
+ productType = "com.apple.product-type.application";
+ };
+ F6BDA801145900D200FE3253 /* SRWebSocketTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F6BDA813145900D200FE3253 /* Build configuration list for PBXNativeTarget "SRWebSocketTests" */;
+ buildPhases = (
+ F6BDA7FD145900D200FE3253 /* Sources */,
+ F6BDA7FE145900D200FE3253 /* Frameworks */,
+ F6BDA7FF145900D200FE3253 /* Resources */,
+ F6BDA800145900D200FE3253 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = SRWebSocketTests;
+ productName = SRWebSocketTests;
+ productReference = F6BDA802145900D200FE3253 /* SRWebSocketTests.octest */;
+ productType = "com.apple.product-type.bundle";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ F6B208241450F597009315AF /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0430;
+ };
+ buildConfigurationList = F6B208271450F597009315AF /* Build configuration list for PBXProject "SocketRocket" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ );
+ mainGroup = F6B208221450F597009315AF;
+ productRefGroup = F6B2082E1450F597009315AF /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ F6B2082C1450F597009315AF /* SocketRocket */,
+ F6B208401450F611009315AF /* SRTests */,
+ F6BDA801145900D200FE3253 /* SRWebSocketTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ F6B2083F1450F611009315AF /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6B2084D1450F611009315AF /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F6BDA7FF145900D200FE3253 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6BDA80C145900D200FE3253 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ F6BDA800145900D200FE3253 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ F6B208291450F597009315AF /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6A12CD2145119B700C1D980 /* SRWebSocket.m in Sources */,
+ F6016C7C146124B20037BB3D /* base64.c in Sources */,
+ F6572126146C7B6A00D6B8A9 /* NSData+SRB64Additions.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F6B2083D1450F611009315AF /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6B2084F1450F611009315AF /* main.m in Sources */,
+ F6B208531450F611009315AF /* SRTAppDelegate.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ F6BDA7FD145900D200FE3253 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F6BDA8161459016900FE3253 /* SRTAutobahnTests.m in Sources */,
+ F6AE4528145907D30022AF3C /* SenTestCase+SRTAdditions.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ F6B2084B1450F611009315AF /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F6B2084C1450F611009315AF /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+ F6BDA80A145900D200FE3253 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F6BDA80B145900D200FE3253 /* en */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ F6B208381450F597009315AF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_ENABLE_OBJC_ARC = YES;
+ COPY_PHASE_STRIP = NO;
+ GCC_C_LANGUAGE_STANDARD = "compiler-default";
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.0;
+ SDKROOT = iphoneos;
+ };
+ name = Debug;
+ };
+ F6B208391450F597009315AF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD_32_BIT)";
+ CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
+ CLANG_ENABLE_OBJC_ARC = YES;
+ COPY_PHASE_STRIP = YES;
+ GCC_C_LANGUAGE_STANDARD = "compiler-default";
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 4.0;
+ SDKROOT = iphoneos;
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ F6B2083B1450F597009315AF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_OBJC_ARC = YES;
+ DSTROOT = /tmp/SocketRocket.dst;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SocketRocket/SocketRocket-Prefix.pch";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib/system\"",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib\"",
+ );
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ };
+ name = Debug;
+ };
+ F6B2083C1450F597009315AF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_OBJC_ARC = YES;
+ DSTROOT = /tmp/SocketRocket.dst;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SocketRocket/SocketRocket-Prefix.pch";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib/system\"",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib\"",
+ );
+ OTHER_LDFLAGS = "-ObjC";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SKIP_INSTALL = YES;
+ };
+ name = Release;
+ };
+ F6B208551450F611009315AF /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_OBJC_ARC = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/Frameworks\"",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SRTests/SRTests-Prefix.pch";
+ INFOPLIST_FILE = "SRTests/SRTests-Info.plist";
+ LD_RUNPATH_SEARCH_PATHS = /Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk;
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib/system\"",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib\"",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Debug;
+ };
+ F6B208561450F611009315AF /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_ENABLE_OBJC_ARC = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/Frameworks\"",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SRTests/SRTests-Prefix.pch";
+ INFOPLIST_FILE = "SRTests/SRTests-Info.plist";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib/system\"",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib\"",
+ );
+ OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ WRAPPER_EXTENSION = app;
+ };
+ name = Release;
+ };
+ F6BDA811145900D200FE3253 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(DEVELOPER_LIBRARY_DIR)/Frameworks",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/Frameworks\"",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SRWebSocketTests/SRWebSocketTests-Prefix.pch";
+ INFOPLIST_FILE = "SRWebSocketTests/SRWebSocketTests-Info.plist";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib/system\"",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib\"",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Debug;
+ };
+ F6BDA812145900D200FE3253 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(SDKROOT)/Developer/Library/Frameworks",
+ "$(DEVELOPER_LIBRARY_DIR)/Frameworks",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/Frameworks\"",
+ );
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = "SRWebSocketTests/SRWebSocketTests-Prefix.pch";
+ INFOPLIST_FILE = "SRWebSocketTests/SRWebSocketTests-Info.plist";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib/system\"",
+ "\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/usr/lib\"",
+ );
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ WRAPPER_EXTENSION = octest;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ F6B208271450F597009315AF /* Build configuration list for PBXProject "SocketRocket" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F6B208381450F597009315AF /* Debug */,
+ F6B208391450F597009315AF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F6B2083A1450F597009315AF /* Build configuration list for PBXNativeTarget "SocketRocket" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F6B2083B1450F597009315AF /* Debug */,
+ F6B2083C1450F597009315AF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F6B208541450F611009315AF /* Build configuration list for PBXNativeTarget "SRTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F6B208551450F611009315AF /* Debug */,
+ F6B208561450F611009315AF /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F6BDA813145900D200FE3253 /* Build configuration list for PBXNativeTarget "SRWebSocketTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F6BDA811145900D200FE3253 /* Debug */,
+ F6BDA812145900D200FE3253 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = F6B208241450F597009315AF /* Project object */;
+}
7 SocketRocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:SocketRocket.xcodeproj">
+ </FileRef>
+</Workspace>
23 SocketRocket/NSData+SRB64Additions.h
@@ -0,0 +1,23 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface NSData (SRB64Additions)
+
+- (NSString *)SR_stringByBase64Encoding;
+
+@end
38 SocketRocket/NSData+SRB64Additions.m
@@ -0,0 +1,38 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import "NSData+SRB64Additions.h"
+#import "base64.h"
+
+@implementation NSData (SRB64Additions)
+
+- (NSString *)SR_stringByBase64Encoding;
+{
+ size_t buffer_size = (([self length] * 3 + 2) / 2);
+
+ char *buffer = (char *)malloc(buffer_size);
+
+ int len = b64_ntop([self bytes], [self length], buffer, buffer_size);
+
+ if (len == -1) {
+ free(buffer);
+ return nil;
+ } else{
+ return [[NSString alloc] initWithBytesNoCopy:buffer length:len encoding:NSUTF8StringEncoding freeWhenDone:YES];
+ }
+}
+
+@end
73 SocketRocket/SRWebSocket.h
@@ -0,0 +1,73 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#import <Foundation/Foundation.h>
+
+typedef enum {
+ SR_CONNECTING = 0,
+ SR_OPEN = 1,
+ SR_CLOSING = 2,
+ SR_CLOSED = 3,
+
+} SRReadyState;
+
+@class SRWebSocket;
+
+typedef void (^SROnOpenHandler)(SRWebSocket *webSocket);
+typedef void (^SROnMessageHandler)(SRWebSocket *webSocket, NSString *message);
+typedef void (^SROnCloseHandler)(SRWebSocket *webSocket, NSInteger code, NSString *reason, BOOL wasClean);
+typedef void (^SROnErrorHandler)(SRWebSocket *webSocket, NSError *error);
+
+extern NSString *const SRWebSocketErrorDomain;
+
+@protocol SRWebSocketDelegate;
+
+@interface SRWebSocket : NSObject <NSStreamDelegate>
+
+@property (nonatomic, assign) id <SRWebSocketDelegate> delegate;
+
+@property (nonatomic, readonly) SRReadyState readyState;
+@property (nonatomic, readonly, strong) NSURL *url;
+
+- (id)initWithURLRequest:(NSURLRequest *)request;
+- (void)connectToHost:(NSString *)host port:(NSInteger)port;
+
+- (void)open;
+- (void)close;
+- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
+
+- (void)failWithError:(NSError *)error;
+
+// Send a UTF8 String or Data
+- (void)send:(id)data;
+
+// Must not be set to nil
+@property (nonatomic, copy) SROnOpenHandler onOpen;
+@property (nonatomic, copy) SROnMessageHandler onMessage;
+@property (nonatomic, copy) SROnCloseHandler onClose;
+@property (nonatomic, copy) SROnErrorHandler onError;
+
+@end
+
+@protocol SRWebSocketDelegate <NSObject>
+@optional
+
+- (void)webSocketDidOpen:(SRWebSocket *)webSocket;
+- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
+- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSString *)message;
+- (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
+
+@end
1,275 SocketRocket/SRWebSocket.m
@@ -0,0 +1,1275 @@
+//
+// Copyright 2012 Square Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+
+#import "SRWebSocket.h"
+
+#import <unicode/utf8.h>
+#import <endian.h>
+#import <CommonCrypto/CommonDigest.h>
+
+#import "base64.h"
+
+typedef enum {
+ SROpCodeTextFrame = 0x1,
+ SROpCodeBinaryFrame = 0x2,
+ //3-7Reserved
+ SROpCodeConnectionClose = 0x8,
+ SROpCodePing = 0x9,
+ SROpCodePong = 0xA,
+ //B-F reserved
+} SROpCode;
+
+typedef enum {
+ SRStatusCodeNormal = 1000,
+ SRStatusCodeGoingAway = 1001,
+ SRStatusCodeProtocolError = 1002,
+ SRStatusCodeUnhandledType = 1003,
+ // 1004-1006 reserved
+ SRStatusCodeInvalidUTF8 = 1007,
+ SRStatusCodePolicyViolated = 1008,
+ SRStatusCodeMessageTooBig = 1009,
+} SRStatusCode;
+
+typedef struct {
+ BOOL fin;
+// BOOL rsv1;
+// BOOL rsv2;
+// BOOL rsv3;
+ uint8_t opcode;
+ BOOL masked;
+ uint64_t payload_length;
+} frame_header;
+
+
+static inline dispatch_queue_t log_queue() {
+
+ static dispatch_queue_t queue = 0;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ queue = dispatch_queue_create("fast log queue", DISPATCH_QUEUE_SERIAL);
+ });
+
+ return queue;
+}
+
+static inline void SRFastLog(NSString *format, ...) {
+
+#if 1
+ __block va_list arg_list;
+ va_start (arg_list, format);
+
+ NSString *formattedString = [[NSString alloc] initWithFormat:format arguments:arg_list];
+
+ va_end(arg_list);
+
+ NSLog(@"SR %@", formattedString);
+#endif
+}
+
+static NSString *const strAppendForAuth = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
+static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
+
+ const void * contents = [data bytes];
+ long size = [data length];
+
+ const uint8_t *str = (const uint8_t *)contents;
+
+
+ UChar32 codepoint = 1;
+ int32_t offset = 0;
+ int32_t lastOffset = 0;
+ while(offset < size && codepoint > 0) {
+ lastOffset = offset;
+ U8_NEXT(str, offset, size, codepoint);
+ }
+
+ if (codepoint == -1) {
+ // Check to see if the last byte is valid or whether it was just continuing
+ if (!U8_IS_LEAD(str[lastOffset]) || U8_COUNT_TRAIL_BYTES(str[lastOffset]) + lastOffset < (int32_t)size) {
+
+ size = -1;
+ } else {
+ uint8_t leadByte = str[lastOffset];
+ U8_MASK_LEAD_BYTE(leadByte, U8_COUNT_TRAIL_BYTES(leadByte));
+
+ for (int i = lastOffset + 1; i < offset; i++) {
+
+ if (U8_IS_SINGLE(str[i]) || U8_IS_LEAD(str[i]) || !U8_IS_TRAIL(str[i])) {
+ size = -1;
+ }
+ }
+
+ if (size != -1) {
+ size = lastOffset;
+ }
+ }
+ }
+
+ if (size != -1 && ![[NSString alloc] initWithBytesNoCopy:(char *)[data bytes] length:size encoding:NSUTF8StringEncoding freeWhenDone:NO]) {
+ size = -1;
+ }
+
+ return size;
+}
+
+
+@interface NSString (DispatchDataAdditions)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+
+@end
+
+#define CONSERVATIVE_COPY
+
+@implementation NSString (DispatchDataAdditions)
+
+- (NSString *)stringBySHA1ThenBase64Encoding;
+{
+ uint8_t md[CC_SHA1_DIGEST_LENGTH];
+
+ CC_SHA1([self UTF8String], [self lengthOfBytesUsingEncoding:NSUTF8StringEncoding], md);
+
+ size_t buffer_size = ((sizeof(md) * 3 + 2) / 2);
+
+ char *buffer = (char *)malloc(buffer_size);
+
+ int len = b64_ntop(md, CC_SHA1_DIGEST_LENGTH, buffer, buffer_size);
+ if (len == -1) {
+ free(buffer);
+ return nil;
+ } else{
+ return [[NSString alloc] initWithBytesNoCopy:buffer length:len encoding:NSASCIIStringEncoding freeWhenDone:YES];
+ }
+}
+
+@end
+
+NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain";
+
+// Returns number of bytes consumed. returning 0 means you didn't match.
+// Sends bytes to callback handler;
+typedef size_t (^stream_scanner)(NSData *collected_data);
+
+typedef void (^data_callback)(SRWebSocket *webSocket, NSData *data);
+
+@interface SRIOConsumer : NSObject {
+ stream_scanner _scanner;
+ data_callback _handler;
+ size_t _bytesNeeded;
+ BOOL _readToCurrentFrame;
+ BOOL _unmaskBytes;
+}
+
+- (id)initWithScanner:(stream_scanner)scanner handler:(data_callback)handler bytesNeeded:(size_t)bytesNeeded readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+
+@property (nonatomic, copy, readonly) stream_scanner consumer;
+@property (nonatomic, copy, readonly) data_callback handler;
+@property (nonatomic, assign) size_t bytesNeeded;
+@property (nonatomic, assign, readonly) BOOL readToCurrentFrame;
+@property (nonatomic, assign, readonly) BOOL unmaskBytes;
+
+@end
+
+
+@interface SRWebSocket () <NSStreamDelegate>
+
+- (void)_writeData:(NSData *)data;
+- (void)_closeWithProtocolError:(NSString *)message;
+- (void)_failWithError:(NSError *)error;
+
+- (void)_disconnect;
+
+- (void)_readFrameNew;
+- (void)_readFrameContinue;
+
+- (void)_pumpScanner;
+
+- (void)_pumpWriting;
+
+- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback;
+- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength;
+- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler;
+- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler;
@zbowling
zbowling added a note

In most cases, private method names generally follow the same rules as public method names. However, a common convention is to give private methods a prefix so it is easy to distinguish them from public methods. Even with this convention, the names given to private methods can cause a peculiar type of problem. When you design a subclass of a Cocoa framework class, you cannot know if your private methods unintentionally override private framework methods that are identically named.

Names of most private methods in the Cocoa frameworks have an underscore prefix (for example, _fooData ) to mark them as private. From this fact follow two recommendations.

  • Don’t use the underscore character as a prefix for your private methods. Apple reserves this convention.
  • If you are subclassing a large Cocoa framework class (such as NSView) and you want to be absolutely sure that your private methods have names different from those in the superclass, you can add your own prefix to your private methods. The prefix should be as unique as possible, perhaps one based on your company or project and of the form "XX_". So if your project is called Byte Flogger, the prefix might be BF_addObject:

Although the advice to give private method names a prefix might seem to contradict the earlier claim that methods exist in the namespace of their class, the intent here is different: to prevent unintentional overriding of superclass private methods.

@mikelikespie Owner

@zbowling Thanks for the feedback. I'll address this issue in future update. I will probably end up going town the SR_ route as I did here:

d0ce5ee#L17R21

Fortunately, we're not subclassing anything but NSObject, so I'm not hugely concerned, but I agree it would be best fo follow their guidelines. They also have private methods that don't have a _ prefix FWIW.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
+- (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data;
+
+- (void)_checkHandshake:(NSDictionary *)headers;
+- (void)_SR_commonInit;
+
++ (dispatch_queue_t)globalReadQueue;
+
+@property (nonatomic) SRReadyState readyState;
+
+@end
+
+
+@implementation SRWebSocket {
+ NSInteger _webSocketVersion;
+ dispatch_queue_t _callbackQueue;
+ dispatch_queue_t _workQueue;
+ NSMutableArray *_consumers;
+
+ NSInputStream *_inputStream;
+ NSOutputStream *_outputStream;
+
+ NSMutableData *_readBuffer;
+ NSInteger _readBufferOffset;
+
+ NSMutableData *_outputBuffer;
+ NSInteger _outputBufferOffset;
+
+ uint8_t _currentFrameOpcode;
+ size_t _currentFrameCount;
+ size_t _readOpCount;
+ uint32_t _currentStringScanPosition;
+ NSMutableData *_currentFrameData;
+
+ uint8_t _currentReadMaskKey[4];
+ size_t _currentReadMaskOffset;
+
+ BOOL _consumerStopped;
+
+ BOOL _closeWhenFinishedWriting;
+
+ BOOL _secure;
+ NSURLRequest *_urlRequest;
+
+ __attribute__((NSObject)) CFHTTPMessageRef _receivedHTTPHeaders;
+
+ BOOL _didFail;
+ int _closeCode;
+}
+
+@synthesize delegate = _delegate;
+@synthesize url = _url;
+@synthesize readyState = _readyState;
+
+@synthesize onOpen = _onOpen;
+@synthesize onClose = _onClose;
+@synthesize onMessage = _onMessage;
+@synthesize onError = _onError;
+
+static __strong NSData *CRLFCRLF;
+
++ (void)initialize;
+{
+ CRLFCRLF = [[NSData alloc] initWithBytes:"\r\n\r\n" length:4];
+}
+
++ (dispatch_queue_t)globalReadQueue;
+{
+ static dispatch_queue_t globalQueue = nil;
+ static dispatch_once_t token;
+
+ dispatch_once(&token, ^{
+ globalQueue = dispatch_queue_create("org.lolrus.socket.globalQueue", DISPATCH_QUEUE_SERIAL);
+ });
+
+ return globalQueue;
+}
+
+- (id)initWithURLRequest:(NSURLRequest *)request;
+{
+ self = [super init];
+ if (self) {
+
+ assert(request.URL);
+ _url = request.URL;
+ NSString *scheme = [_url scheme];
+
+ assert([scheme isEqualToString:@"ws"] || [scheme isEqualToString:@"http"] || [scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]);
+ _urlRequest = request;
+
+ if ([scheme isEqualToString:@"wss"] || [scheme isEqualToString:@"https"]) {
+ _secure = YES;
+ }
+
+ [self _SR_commonInit];
+ }
+
+ return self;
+}
+
+- (void)_SR_commonInit;
+{
+ _readyState = SR_CONNECTING;
+
+ _consumerStopped = YES;
+
+ _webSocketVersion = 13;
+
+ _workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
+
+ _callbackQueue = dispatch_get_main_queue();
+ dispatch_retain(_callbackQueue);
+
+ _readBuffer = [[NSMutableData alloc] init];
+ _outputBuffer = [[NSMutableData alloc] init];
+
+ _currentFrameData = [[NSMutableData alloc] init];
+
+ _consumers = [[NSMutableArray alloc] init];
+
+ // default handlers
+ self.onError = ^(SRWebSocket *webSocket, NSError *error) {
+ if ([webSocket.delegate respondsToSelector:@selector(webSocket:didFailWithError:)]) {
+ [webSocket.delegate webSocket:webSocket didFailWithError:error];
+ }
+ };
+
+ self.onMessage = ^(SRWebSocket *webSocket, id message) {
+ if ([webSocket.delegate respondsToSelector:@selector(webSocket:didReceiveMessage:)]) {
+ [webSocket.delegate webSocket:webSocket didReceiveMessage:message];
+ }
+ };
+
+ self.onClose = ^(SRWebSocket *webSocket, NSInteger code, NSString *reason, BOOL wasClean) {
+ if ([webSocket.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
+ [webSocket.delegate webSocket:webSocket didCloseWithCode:code reason:reason wasClean:wasClean];
+ }
+ };
+
+ self.onOpen = ^(SRWebSocket *webSocket) {
+ if ([webSocket.delegate respondsToSelector:@selector(webSocketDidOpen:)]) {
+ [webSocket.delegate webSocketDidOpen:webSocket];
+ }
+ };
+}
+
+- (void)dealloc
+{
+ dispatch_release(_callbackQueue);
+ dispatch_release(_workQueue);
+ _inputStream.delegate = nil;
+ _outputStream.delegate = nil;
+}
+
+#ifndef NDEBUG
+
+- (void)setReadyState:(SRReadyState)aReadyState;
+{
+ [self willChangeValueForKey:@"readyState"];
+ assert(aReadyState > _readyState);
+ _readyState = aReadyState;
+ [self didChangeValueForKey:@"readyState"];
+}
+
+#endif
+
+- (void)open {
+ assert(_url);
+
+ NSInteger port = _url.port.integerValue;
+ if (port == 0) {
+ if (!_secure) {
+ port = 80;
+ } else {
+ port = 443;
+ }
+ }
+
+ [self connectToHost:_url.host port:port];
+}
+
+
+
+- (void)_checkHandshake:(NSDictionary *)headers;
+{
+ SRFastLog(@"TODO: Add handshake checking");
+}
+
+- (void)_HTTPHeadersDidFinish;
+{
+ NSDictionary *dict = CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders));
+
+ [self _checkHandshake:dict];
+ NSInteger responseCode = CFHTTPMessageGetResponseStatusCode(_receivedHTTPHeaders);
+
+ if (responseCode >= 400) {
+ SRFastLog(@"Request failed with response code %d", responseCode);
+ [self failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:2132 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"received bad response code from server %d", responseCode] forKey:NSLocalizedDescriptionKey]]];
+ return;
+
+ }
+
+ self.readyState = SR_OPEN;
+
+ if (!_didFail) {
+ [self _readFrameNew];
+ }
+
+ dispatch_async(_callbackQueue, ^{
+ self.onOpen(self);
+ });
+}
+
+
+- (void)_readHTTPHeader;
+{
+ if (_receivedHTTPHeaders == NULL) {
+ _receivedHTTPHeaders = CFHTTPMessageCreateEmpty(NULL, NO);
+ }
+
+ [self _readUntilHeaderCompleteWithCallback:^(SRWebSocket *self, NSData *data) {
+ CFHTTPMessageAppendBytes(_receivedHTTPHeaders, (const UInt8 *)data.bytes, data.length);
+
+ if (CFHTTPMessageIsHeaderComplete(_receivedHTTPHeaders)) {
+ SRFastLog(@"Finished reading headers %@", CFBridgingRelease(CFHTTPMessageCopyAllHeaderFields(_receivedHTTPHeaders)));
+ [self _HTTPHeadersDidFinish];
+ } else {
+ [self _readHTTPHeader];
+ }
+ }];
+}
+
+- (void)didConnect
+{
+ SRFastLog(@"Connected");
+ CFHTTPMessageRef request = CFHTTPMessageCreateRequest(NULL, CFSTR("GET"), (__bridge CFURLRef)_url, kCFHTTPVersion1_1);
+
+ // Set host first so it defaults
+ CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host));
+
+ [_urlRequest.allHTTPHeaderFields enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
+ CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)key, (__bridge CFStringRef)obj);
+ }];
+
+ CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket"));
+ CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade"));
+ CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), CFSTR("/PiDVHFKG9+oB7rLAudvxw=="));
+ CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Version"), (__bridge CFStringRef)[NSString stringWithFormat:@"%d", _webSocketVersion]);
+ CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Origin"), (__bridge CFStringRef)_url.absoluteString);
+
+ NSData *message = CFBridgingRelease(CFHTTPMessageCopySerializedMessage(request));
+
+ CFRelease(request);
+
+ [self _writeData:message];
+ [self _readHTTPHeader];
+}
+
+- (void)connectToHost:(NSString *)host port:(NSInteger)port;
+{
+ CFReadStreamRef readStream = NULL;
+ CFWriteStreamRef writeStream = NULL;
+
+ CFStreamCreatePairWithSocketToHost(NULL, (__bridge CFStringRef)host, port, &readStream, &writeStream);
+
+ _outputStream = CFBridgingRelease(writeStream);
+ _inputStream = CFBridgingRelease(readStream);
+
+ if (_secure) {
+ [_outputStream setProperty:(__bridge id)kCFStreamSocketSecurityLevelNegotiatedSSL forKey:(__bridge id)kCFStreamPropertySocketSecurityLevel];
+ #if DEBUG
+ NSLog(@"SocketRocket: In debug mode. Allowing connection to any root cert");
+ [_outputStream setProperty:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES]
+ forKey:(__bridge id)kCFStreamSSLAllowsAnyRoot]
+ forKey:(__bridge id)kCFStreamPropertySSLSettings];
+ #endif
+ }
+
+ _inputStream.delegate = self;
+ _outputStream.delegate = self;
+
+ // TODO schedule in a better run loop
+ [_outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
+ [_inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
+
+
+ [_outputStream open];
+ [_inputStream open];
+}
+
+- (void)close;
+{
+ [self closeWithCode:-1 reason:nil];
+}
+
+- (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
+{
+ assert(code);
+ if (self.readyState == SR_CLOSING || self.readyState == SR_CLOSED) {
+ return;
+ }
+
+ BOOL wasConnecting = self.readyState == SR_CONNECTING;
+
+ self.readyState = SR_CLOSING;
+
+ SRFastLog(@"Closing with code %d reason %@", code, reason);
+ dispatch_async(_workQueue, ^{
+ if (wasConnecting) {
+ [self _disconnect];
+ return;
+ }
+
+ size_t maxMsgSize = [reason maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+ NSMutableData *mutablePayload = [[NSMutableData alloc] initWithLength:sizeof(uint16_t) + maxMsgSize];
+ NSData *payload = mutablePayload;
+
+ ((uint16_t *)mutablePayload.mutableBytes)[0] = EndianU16_BtoN(code);
+
+ if (reason) {
+ NSRange remainingRange = {0};
+
+ NSUInteger usedLength = 0;
+
+ BOOL success = [reason getBytes:(char *)mutablePayload.mutableBytes + sizeof(uint16_t) maxLength:payload.length - sizeof(uint16_t) usedLength:&usedLength encoding:NSUTF8StringEncoding options:NSStringEncodingConversionExternalRepresentation range:NSMakeRange(0, reason.length) remainingRange:&remainingRange];
+
+ assert(success);
+ assert(remainingRange.length == 0);
+
+ if (usedLength != maxMsgSize) {
+ payload = [payload subdataWithRange:NSMakeRange(0, usedLength + sizeof(uint16_t))];
+ }
+ }
+
+
+ [self _sendFrameWithOpcode:SROpCodeConnectionClose data:payload];
+ });
+}
+
+- (void)failWithError:(NSError *)error;
+{
+ [self _failWithError:error];
+}
+
+- (void)_closeWithProtocolError:(NSString *)message;
+{
+ [self closeWithCode:SRStatusCodeProtocolError reason:message];
+ dispatch_async(_workQueue, ^{
+ [self _disconnect];
+ });
+}
+
+- (void)_failWithError:(NSError *)error;
+{
+ dispatch_async(_workQueue, ^{
+ if (self.readyState != SR_CLOSED) {
+ dispatch_async(_callbackQueue, ^{
+ _onError(self, error);
+ });
+
+ self.readyState = SR_CLOSED;
+
+ SRFastLog(@"Failing with error %@", error.localizedDescription);
+
+ [self _disconnect];
+ }
+ });
+}
+
+- (void)_writeData:(NSData *)data;
+{
+ assert(dispatch_get_current_queue() == _workQueue);
+
+ if (_closeWhenFinishedWriting) {
+ return;
+ }
+ [_outputBuffer appendData:data];
+ [self _pumpWriting];
+}
+- (void)send:(id)data;
+{
+ // TODO: maybe not copy this for performance
+ data = [data copy];
+ dispatch_async(_workQueue, ^{
+ if ([data isKindOfClass:[NSString class]]) {
+ [self _sendFrameWithOpcode:SROpCodeTextFrame data:[(NSString *)data dataUsingEncoding:NSUTF8StringEncoding]];
+ } else if ([data isKindOfClass:[NSData class]]) {
+ [self _sendFrameWithOpcode:SROpCodeBinaryFrame data:data];
+ } else if (data == nil) {
+ [self _sendFrameWithOpcode:SROpCodeTextFrame data:data];
+ } else {
+ assert(NO);
+ }
+ });
+}
+
+- (void)handlePing:(NSData *)pingData;
+{
+ [self _sendFrameWithOpcode:SROpCodePong data:pingData];
+}
+
+- (void)handlePong;
+{
+ // NOOP
+}
+
+- (void)handleMessage:(id)message
+{
+ dispatch_async(_callbackQueue, ^{
+ _onMessage(self, message);
+ });
+}
+
+// Note from RFC:
+//
+// If there is a body, the first two
+// bytes of the body MUST be a 2-byte unsigned integer (in network byte
+// order) representing a status code with value /code/ defined in
+// Section 7.4. Following the 2-byte integer the body MAY contain UTF-8
+// encoded data with value /reason/, the interpretation of which is not
+// defined by this specification.
+
+- (void)handleCloseWithData:(NSData *)data;
+{
+ size_t dataSize = data.length;
+ __block uint16_t closeCode = 0;
+
+ NSString *reason = nil;
+
+ SRFastLog(@"Received close frame");
+
+ if (dataSize == 1) {
+ // TODO handle error
+// assert(NO);
+ } else if (dataSize >= 2) {
+ [data getBytes:&closeCode length:sizeof(closeCode)];
+ closeCode = EndianU16_BtoN(closeCode);
+
+ if (dataSize > 2) {
+ reason = [[NSString alloc] initWithData:[data subdataWithRange:NSMakeRange(2, dataSize - 2)] encoding:NSUTF8StringEncoding];
+ }
+ }
+
+ assert(dispatch_get_current_queue() == _workQueue);
+
+ dispatch_async(_workQueue, ^{
+ if (self.readyState == SR_OPEN) {
+ [self closeWithCode:SRStatusCodeNormal reason:reason];
+ }
+ [self _disconnect];
+ });
+}
+
+- (void)_disconnect;
+{
+ SRFastLog(@"Trying to disconnect");
+ dispatch_async(_workQueue, ^{
+ _closeWhenFinishedWriting = YES;
+ [self _pumpWriting];
+ });
+}
+
+- (void)_handleFrameWithData:(NSData *)frameData opCode:(NSInteger)opcode;
+{
+ // Check that the current data is valid UTF8
+
+ BOOL isControlFrame = (opcode == SROpCodePing || opcode == SROpCodePong || opcode == SROpCodeConnectionClose);
+ if (!isControlFrame) {
+ [self _readFrameNew];
+ } else {
+ dispatch_async(_workQueue, ^{
+ [self _readFrameContinue];
+ });
+ }
+
+ switch (opcode) {
+ case SROpCodeTextFrame: {
+ NSString *str = [[NSString alloc] initWithData:frameData encoding:NSUTF8StringEncoding];
+ if (str == nil && frameData) {
+ [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"];
+ dispatch_async(_workQueue, ^{
+ [self _disconnect];
+ });
+
+ return;
+ }
+ [self handleMessage:str];
+ break;
+ }
+ case SROpCodeBinaryFrame:
+ [self handleMessage:[frameData copy]];
+ break;
+ case SROpCodeConnectionClose:
+ [self handleCloseWithData:frameData];
+ break;
+ case SROpCodePing:
+ [self handlePing:frameData];
+ break;
+ case SROpCodePong:
+ [self handlePong];
+ break;
+ default:
+ [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %d", opcode]];
+ // TODO: Handle invalid opcode
+ break;
+ }
+}
+
+- (void)_handleFrameHeader:(frame_header)frame_header curData:(NSData *)curData;
+{
+ assert(frame_header.opcode != 0);
+
+ if (self.readyState != SR_OPEN) {
+ return;
+ }
+
+
+ BOOL isControlFrame = (frame_header.opcode == SROpCodePing || frame_header.opcode == SROpCodePong || frame_header.opcode == SROpCodeConnectionClose);
+
+ if (isControlFrame && !frame_header.fin) {
+ [self _closeWithProtocolError:@"Fragmented control frames not allowed"];
+ return;
+ }
+
+ if (isControlFrame && frame_header.payload_length >= 126) {
+ [self _closeWithProtocolError:@"Control frames cannot have payloads larger than 126 bytes"];
+ return;
+ }
+
+ if (!isControlFrame) {
+ _currentFrameOpcode = frame_header.opcode;
+ _currentFrameCount += 1;
+ }
+
+ if (frame_header.payload_length == 0) {
+ if (isControlFrame) {
+ [self _handleFrameWithData:[NSData data] opCode:frame_header.opcode];
+ } else {
+ if (frame_header.fin) {
+// assert(_currentFrameData.length == frame_header.payload_length);
+ [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode];
+ } else {
+ // TODO add assert that opcode is not a control;
+ [self _readFrameContinue];
+ }
+ }
+ } else {
+ [self _addConsumerWithDataLength:frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) {
+ if (isControlFrame) {
+ [self _handleFrameWithData:newData opCode:frame_header.opcode];
+ } else {
+ if (frame_header.fin) {
+ [self _handleFrameWithData:_currentFrameData opCode:frame_header.opcode];
+ } else {
+ // TODO add assert that opcode is not a control;
+ [self _readFrameContinue];
+ }
+
+ }
+ } readToCurrentFrame:!isControlFrame unmaskBytes:frame_header.masked];
+ }
+}
+
+/* From RFC:
+
+ 0 1 2 3
+ 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
+ +-+-+-+-+-------+-+-------------+-------------------------------+
+ |F|R|R|R| opcode|M| Payload len | Extended payload length |
+ |I|S|S|S| (4) |A| (7) | (16/64) |
+ |N|V|V|V| |S| | (if payload len==126/127) |
+ | |1|2|3| |K| | |
+ +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
+ | Extended payload length continued, if payload len == 127 |
+ + - - - - - - - - - - - - - - - +-------------------------------+
+ | |Masking-key, if MASK set to 1 |
+ +-------------------------------+-------------------------------+
+ | Masking-key (continued) | Payload Data |
+ +-------------------------------- - - - - - - - - - - - - - - - +
+ : Payload Data continued ... :
+ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ | Payload Data continued ... |
+ +---------------------------------------------------------------+
+ */
+
+static const uint8_t SRFinMask = 0x80;
+static const uint8_t SROpCodeMask = 0x0F;
+static const uint8_t SRRsvMask = 0x70;
+static const uint8_t SRMaskMask = 0x80;
+static const uint8_t SRPayloadLenMask = 0x7F;
+
+
+- (void)_readFrameContinue;
+{
+ assert((_currentFrameCount == 0 && _currentFrameOpcode == 0) || (_currentFrameCount > 0 && _currentFrameOpcode > 0));
+
+ [self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) {
+ __block frame_header header = {0};
+
+
+ const uint8_t *headerBuffer = data.bytes;
+ assert(data.length >= 2);
+
+ if (headerBuffer[0] & SRRsvMask) {
+ [(__unsafe_unretained SRWebSocket *)self _closeWithProtocolError:@"Server used RSV bits"];
+ return;
+ }
+
+ uint8_t receivedOpcode = (SROpCodeMask & headerBuffer[0]);
+
+ BOOL isControlFrame = (receivedOpcode == SROpCodePing || receivedOpcode == SROpCodePong || receivedOpcode == SROpCodeConnectionClose);
+
+ if (!isControlFrame && receivedOpcode != 0 && self->_currentFrameCount > 0) {
+ [self _closeWithProtocolError:@"all data frames after the initial data frame must have opcode 0"];
+ return;
+ }
+
+ if (receivedOpcode == 0 && _currentFrameCount == 0) {
+ [self _closeWithProtocolError:@"cannot continue a message"];
+ return;
+ }
+
+ header.opcode = receivedOpcode == 0 ? _currentFrameOpcode : receivedOpcode;
+
+ header.fin = !!(SRFinMask & headerBuffer[0]);
+
+
+ header.masked = !!(SRMaskMask & headerBuffer[1]);
+ header.payload_length = SRPayloadLenMask & headerBuffer[1];
+
+ headerBuffer = NULL;
+
+ if (header.masked) {
+ [self _closeWithProtocolError:@"Client must receive unmasked data"];
+ }
+
+ size_t extra_bytes_needed = header.masked ? sizeof(_currentReadMaskKey) : 0;
+
+ if (header.payload_length == 126) {
+ extra_bytes_needed += sizeof(uint16_t);
+ } else if (header.payload_length == 127) {
+ extra_bytes_needed += sizeof(uint64_t);
+ }
+
+ if (extra_bytes_needed == 0) {
+ [self _handleFrameHeader:header curData:_currentFrameData];
+ } else {
+ [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) {
+ size_t mapped_size = data.length;
+ const void *mapped_buffer = data.bytes;
+ size_t offset = 0;
+
+ if (header.payload_length == 126) {
+ assert(mapped_size >= sizeof(uint16_t));
+ uint16_t newLen = EndianU16_BtoN(*(uint16_t *)(mapped_buffer));
+ header.payload_length = newLen;
+ offset += sizeof(uint16_t);
+ } else if (header.payload_length == 127) {
+ assert(mapped_size >= sizeof(uint64_t));
+ header.payload_length = EndianU64_BtoN(*(uint64_t *)(mapped_buffer));
+ offset += sizeof(uint64_t);
+ } else {
+ assert(header.payload_length < 126 && header.payload_length >= 0);
+ }
+
+
+ if (header.masked) {
+ assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset);
+ memcpy(_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(_currentReadMaskKey));
+ }
+
+ [self _handleFrameHeader:header curData:_currentFrameData];
+ } readToCurrentFrame:NO unmaskBytes:NO];
+ }
+ } readToCurrentFrame:NO unmaskBytes:NO];
+}
+
+- (void)_readFrameNew;
+{
+ dispatch_async(_workQueue, ^{
+ [_currentFrameData setLength:0];
+
+ _currentFrameOpcode = 0;
+ _currentFrameCount = 0;
+ _readOpCount = 0;
+ _currentStringScanPosition = 0;
+
+ [self _readFrameContinue];
+ });
+}
+
+- (void)_pumpWriting;
+{
+ assert(dispatch_get_current_queue() == _workQueue);
+
+ NSUInteger dataLength = _outputBuffer.length;
+ if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) {
+ NSUInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset];
+ if (bytesWritten == -1) {
+ [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]];
+ return;
+ }
+
+ _outputBufferOffset += bytesWritten;
+
+ if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) {
+ _outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset];
+ _outputBufferOffset = 0;
+ }
+
+ }
+
+ if (_closeWhenFinishedWriting && _outputBuffer.length - _outputBufferOffset == 0 && (_inputStream.streamStatus != NSStreamStatusNotOpen && _inputStream.streamStatus != NSStreamStatusClosed)) {
+ [_outputStream close];
+ [_inputStream close];
+
+ dispatch_async(_callbackQueue, ^{
+ _onClose(self, _closeCode, nil, YES);
+ });
+ }
+}
+
+- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback;
+{
+ [self _addConsumerWithScanner:consumer callback:callback dataLength:0];
+}
+
+- (void)_addConsumerWithDataLength:(size_t)dataLength callback:(data_callback)callback readToCurrentFrame:(BOOL)readToCurrentFrame unmaskBytes:(BOOL)unmaskBytes;
+{
+ assert(dataLength);
+
+ dispatch_async(_workQueue, ^{
+ [_consumers addObject:[[SRIOConsumer alloc] initWithScanner:nil handler:callback bytesNeeded:dataLength readToCurrentFrame:readToCurrentFrame unmaskBytes:unmaskBytes]];
+ [self _pumpScanner];
+ });
+}
+
+- (void)_addConsumerWithScanner:(stream_scanner)consumer callback:(data_callback)callback dataLength:(size_t)dataLength;
+{
+ dispatch_async(_workQueue, ^{
+ [_consumers addObject:[[SRIOConsumer alloc] initWithScanner:consumer handler:callback bytesNeeded:dataLength readToCurrentFrame:NO unmaskBytes:NO]];
+ [self _pumpScanner];
+ });
+}
+
+
+static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
+
+- (void)_readUntilHeaderCompleteWithCallback:(data_callback)dataHandler;
+{
+ [self _readUntilBytes:CRLFCRLFBytes length:sizeof(CRLFCRLFBytes) callback:dataHandler];
+}
+
+- (void)_readUntilBytes:(const void *)bytes length:(size_t)length callback:(data_callback)dataHandler;
+{
+ // TODO optimize so this can continue from where we last searched
+ stream_scanner consumer = ^size_t(NSData *data) {
+ __block size_t found_size = 0;
+ __block size_t match_count = 0;
+
+ size_t size = data.length;
+ const unsigned char *buffer = data.bytes;
+ for (int i = 0; i < size; i++ ) {
+ if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) {
+ match_count += 1;
+ if (match_count == length) {
+ found_size = i + 1;
+ break;
+ }
+ } else {
+ match_count = 0;
+ }
+ }
+ return found_size;
+ };
+ [self _addConsumerWithScanner:consumer callback:dataHandler];
+}
+
+-(void)_pumpScanner;
+{
+ assert(dispatch_get_current_queue() == _workQueue);
+
+ if (self.readyState >= SR_CLOSING) {
+ return;
+ }
+
+ if (!_consumers.count) {
+ return;
+ }
+
+ size_t curSize = _readBuffer.length - _readBufferOffset;
+ if (!curSize) {
+ return;
+ }
+
+ SRIOConsumer *consumer = [_consumers objectAtIndex:0];
+
+ size_t bytesNeeded = consumer.bytesNeeded;
+
+ size_t foundSize = 0;
+ if (consumer.consumer) {
+ NSData *tempView = [NSData dataWithBytesNoCopy:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset freeWhenDone:NO];
+ foundSize = consumer.consumer(tempView);
+ } else {
+ assert(consumer.bytesNeeded);
+ if (curSize >= bytesNeeded) {
+ foundSize = bytesNeeded;
+ } else if (consumer.readToCurrentFrame) {
+ foundSize = curSize;
+ }
+ }
+
+ NSData *slice = nil;
+ if (consumer.readToCurrentFrame || foundSize) {
+ NSRange sliceRange = NSMakeRange(_readBufferOffset, foundSize);
+ slice = [_readBuffer subdataWithRange:sliceRange];
+
+ _readBufferOffset += foundSize;
+
+ if (_readBufferOffset > 4096 && _readBufferOffset > (_readBuffer.length >> 1)) {
+ _readBuffer = [[NSMutableData alloc] initWithBytes:(char *)_readBuffer.bytes + _readBufferOffset length:_readBuffer.length - _readBufferOffset]; _readBufferOffset = 0;
+ }