Browse Source

Merge branch 'original_master' into cert-trust

John MacKanacKy 10 years ago
parent
commit
23dd00dd7e

+ 16 - 0
.gitignore

@@ -6,3 +6,19 @@ build/
 nohup.out
 .DS_Store
 xcuserdata/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+*.xcworkspace
+!default.xcworkspace
+*xcuserdata
+*.xccheckout
+profile
+*.moved-aside
+DerivedData
+extern/

+ 8 - 0
.travis.yml

@@ -0,0 +1,8 @@
+language: objective-c
+osx_image: xcode6.4
+before_script:
+    - bundle install
+script:
+    - xcodebuild -project SocketRocket.xcodeproj -scheme "SocketRocket" -sdk iphonesimulator -configuration Debug -PBXBuildsContinueAfterErrors=0 ACTIVE_ARCH_ONLY=0 build test
+    - xcodebuild -project SocketRocket.xcodeproj -scheme "SocketRocketOSX" -sdk macosx10.10 -configuration Debug -destination "platform=OS X" -PBXBuildsContinueAfterErrors=0 build
+    - pod lib lint --verbose --fail-fast

+ 3 - 0
Gemfile

@@ -0,0 +1,3 @@
+source 'https://rubygems.org'
+
+gem 'cocoapods', '0.37.2'

+ 6 - 0
Makefile

@@ -1,6 +1,12 @@
 TEST_SCENARIOS="[1-8]*"
 TEST_URL='ws://localhost:9001/'
 
+all:
+	$(MAKE) -C SocketRocket
+
+clean:
+	$(MAKE) -C SocketRocket clean
+
 test:
 
 	mkdir -p pages/results

+ 5 - 7
README.rst

@@ -8,7 +8,7 @@ You can compare to what `modern browsers look like here
 <http://www.tavendo.de/autobahn/testsuite/report/clients/index.html>`_.
 
 SocketRocket currently conforms to all ~300 of `Autobahn
-<http://www.tavendo.de/autobahn/testsuite.html>`_'s fuzzing tests (aside from
+<http://autobahn.ws/testsuite/>`_'s fuzzing tests (aside from
 two UTF-8 ones where it is merely *non-strict*. tests 6.4.2 and 6.4.4)
 
 Features/Design
@@ -56,7 +56,7 @@ There's a few options. Choose one, or just figure it out
 
 
 Depending on how you configure your project you may need to ``#import`` either
-``<SocketRocket/SRSocketRocket.h>`` or ``"SRSocketRocket.h"``
+``<SocketRocket/SRWebSocket.h>`` or ``"SRWebSocket.h"``
 
 Framework Dependencies
 ``````````````````````
@@ -73,7 +73,7 @@ SocketRocket now has (64-bit only) OS X support.  ``SocketRocket.framework``
 inside Xcode project is for OS X only.  It should be identical in function aside
 from the unicode validation.  ICU isn't shipped with OS X which is what the
 original implementation used for unicode validation.  The workaround is much
-more rhudimentary and less robust.
+more rudimentary and less robust.
 
 1. Add SocketRocket.xcodeproj as either a subproject of your app or in your workspace.
 2. Add ``SocketRocket.framework`` to the link libraries
@@ -222,9 +222,7 @@ WebSocket Server Implementation Recommendations
 SocketRocket has been used with the following libraries:
 
 - `Tornado <https://github.com/facebook/tornado>`_
-- Go's `weekly build <http://weekly.golang.org>`_ (the official release has an
-  outdated protocol, so you may have to use weekly until `Go 1
-  <http://blog.golang.org/2011/10/preview-of-go-version-1.html>`_ is released)
+- Go's `WebSocket package <http://godoc.org/code.google.com/p/go.net/websocket>`_ or Gorilla's `version <http://www.gorillatoolkit.org/pkg/websocket>`_
 - `Autobahn <http://www.tavendo.de/autobahn/testsuite.html>`_ (using its fuzzing
   client)
 
@@ -239,7 +237,7 @@ It could use some more control over things such as pings, etc., but I
 am sure it will come in time.
 
 Autobahn is a great test suite.  The Python server code is good, and conforms
-well (obviously).  Hovever, for me, twisted would be a deal-breaker for writing
+well (obviously).  However for me, twisted would be a deal-breaker for writing
 something new.  I find it a bit too complex and heavy for a simple service. If
 you are already using twisted though, Autobahn is probably for you.
 

+ 16 - 21
SRWebSocketTests/SRTAutobahnTests.m

@@ -14,16 +14,16 @@
 //   limitations under the License.
 //
 
-#import <SenTestingKit/SenTestingKit.h>
-#import <SenTestingKit/SenTestRun.h>
-#import "SRWebSocket.h"
+#import <XCTest/XCTest.h>
+#import <XCTest/XCTestRun.h>
+#import <SocketRocket/SRWebSocket.h>
 #import "SRTWebSocketOperation.h"
 #import "SenTestCase+SRTAdditions.h"
 
 #define SRLogDebug(format, ...) 
 //#define SRLogDebug(format, ...) NSLog(format, __VA_ARGS__)
 
-@interface SRTAutobahnTests : SenTestCase
+@interface SRTAutobahnTests : XCTestCase
 @end
 
 @interface TestOperation : SRTWebSocketOperation <SRWebSocketDelegate>
@@ -98,7 +98,6 @@
 {
     self = [super initWithInvocation:anInvocation];
     if (self) {
-        [self raiseAfterFailure];
         _testURLString = [[NSProcessInfo processInfo].environment objectForKey:@"SR_TEST_URL"];
         _prefixURL = [NSURL URLWithString:_testURLString];
         _agent = [NSBundle bundleForClass:[self class]].bundleIdentifier;
@@ -106,7 +105,7 @@
     return self;
 }
 
-- (unsigned int)testCaseCount;
+- (NSUInteger)testCaseCount;
 {
     if (self.invocation) {
         return [super testCaseCount];
@@ -120,7 +119,7 @@
         return caseGetter.isFinished;
     } timeout:20.0];
     
-    STAssertNil(caseGetter.error, @"CaseGetter should have successfully returned the number of testCases. Instead got error %@", caseGetter.error);
+    XCTAssertNil(caseGetter.error, @"CaseGetter should have successfully returned the number of testCases. Instead got error %@", caseGetter.error);
     
     NSInteger caseCount = caseGetter.caseCount;
     
@@ -132,7 +131,7 @@
     return NO;
 }
 
-- (void)performTest:(SenTestCaseRun *) aRun
+- (void)performTest:(XCTestCaseRun *) aRun
 {
     if (self.invocation) {
         [super performTest:aRun];
@@ -150,15 +149,11 @@
         
         NSString *description = [self caseDescriptionForCaseNumber:i];
 
-        SenTestCase *testCase = [[[self class] alloc] initWithInvocation:invocation description:description];
+        XCTestCase *testCase = [[[self class] alloc] initWithInvocation:invocation description:description];
         
-        SenTestCaseRun *run = [[SenTestCaseRun alloc] initWithTest:testCase];
+        XCTestCaseRun *run = [[XCTestCaseRun alloc] initWithTest:testCase];
         
         [testCase performTest:run];
-        
-        for (NSException *e in run.exceptions) {
-            [aRun addException:e];
-        }
     }
     [aRun stop];
     
@@ -182,7 +177,7 @@
         return testInfoOperation.isFinished;
     } timeout:60 * 60];
     
-    STAssertNil(testInfoOperation.error, @"Updating the report should not have errored");
+    XCTAssertNil(testInfoOperation.error, @"Updating the report should not have errored");
     
     return [NSString stringWithFormat:@"%@ - %@", [testInfoOperation.info objectForKey:@"id"], [testInfoOperation.info objectForKey:@"description"]];
 }
@@ -220,8 +215,8 @@
         return resultOp.isFinished;
     } timeout:60 * 60];
     
-    STAssertTrue(!testOp.error, @"Test operation should not have failed");
-    STAssertEqualObjects(@"OK", [resultOp.info objectForKey:@"behavior"], @"Test behavior should be OK");
+    XCTAssertTrue(!testOp.error, @"Test operation should not have failed");
+    XCTAssertEqualObjects(@"OK", [resultOp.info objectForKey:@"behavior"], @"Test behavior should be OK");
 }
 
 - (void)updateReports;
@@ -234,7 +229,7 @@
         return updateReportOperation.isFinished;
     } timeout:60 * 60];
     
-    STAssertNil(updateReportOperation.error, @"Updating the report should not have errored");
+    XCTAssertNil(updateReportOperation.error, @"Updating the report should not have errored");
 }
 
 @end
@@ -247,7 +242,7 @@
 {   
     
     NSString *path = [[url URLByAppendingPathComponent:@"runCase"] absoluteString];
-    path = [path stringByAppendingFormat:@"?case=%d&agent=%@", testNumber, agent];
+    path = [path stringByAppendingFormat:@"?case=%@&agent=%@", @(testNumber), agent];
     
     self = [super initWithURL:[NSURL URLWithString:path]];
     if (self) {
@@ -310,7 +305,7 @@
 - (id)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber;
 {
     NSString *path = [[url URLByAppendingPathComponent:@"getCaseInfo"] absoluteString];
-    path = [path stringByAppendingFormat:@"?case=%d", caseNumber];
+    path = [path stringByAppendingFormat:@"?case=%@", @(caseNumber)];
     
     return [super initWithURL:[NSURL URLWithString:path]];
 }
@@ -330,7 +325,7 @@
 - (id)initWithBaseURL:(NSURL *)url caseNumber:(NSInteger)caseNumber agent:(NSString *)agent;
 {
     NSString *path = [[url URLByAppendingPathComponent:@"getCaseStatus"] absoluteString];
-    path = [path stringByAppendingFormat:@"?case=%d&agent=%@", caseNumber, agent];
+    path = [path stringByAppendingFormat:@"?case=%@&agent=%@", @(caseNumber), agent];
     
     return [super initWithURL:[NSURL URLWithString:path]];
 }

+ 1 - 1
SRWebSocketTests/SRTWebSocketOperation.h

@@ -8,7 +8,7 @@
 
 #import <Foundation/Foundation.h>
 
-#import "SRWebSocket.h"
+#import <SocketRocket/SRWebSocket.h>
 
 @interface SRTWebSocketOperation : NSOperation <SRWebSocketDelegate>
 

+ 0 - 1
SRWebSocketTests/SRTWebSocketOperation.m

@@ -7,7 +7,6 @@
 //
 
 #import "SRTWebSocketOperation.h"
-#import "SRWebSocket.h"
 
 @interface SRTWebSocketOperation ()
 

+ 1 - 1
SRWebSocketTests/SRWebSocketTests-Prefix.pch

@@ -16,6 +16,6 @@
 
 #ifdef __OBJC__
     #import <Foundation/Foundation.h>
-    #import <SenTestingKit/SenTestingKit.h>
+    #import <XCTest/XCTest.h>
     #import "SenTestCase+SRTAdditions.h"
 #endif

+ 2 - 2
SRWebSocketTests/SenTestCase+SRTAdditions.h

@@ -14,13 +14,13 @@
 //   limitations under the License.
 //
 
-#import <SenTestingKit/SenTestingKit.h>
+#import <XCTest/XCTest.h>
 
 
 typedef BOOL (^PXPredicateBlock)();
 
 
-@interface SenTest (PXAdditions)
+@interface XCTest (PXAdditions)
 
 - (void)runCurrentRunLoopUntilTestPasses:(PXPredicateBlock)predicate timeout:(NSTimeInterval)timeout;
 

+ 2 - 2
SRWebSocketTests/SenTestCase+SRTAdditions.m

@@ -17,7 +17,7 @@
 #import "SenTestCase+SRTAdditions.h"
 
 
-@implementation SenTestCase (SRTAdditions)
+@implementation XCTestCase (SRTAdditions)
 
 - (void)runCurrentRunLoopUntilTestPasses:(PXPredicateBlock)predicate timeout:(NSTimeInterval)timeout;
 {
@@ -32,7 +32,7 @@
         [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
     }
     
-    STAssertTrue(currentTime <= timeoutTime, @"Timed out");
+    XCTAssertTrue(currentTime <= timeoutTime, @"Timed out");
 }
 
 @end

+ 10 - 6
SocketRocket.podspec

@@ -1,15 +1,19 @@
 Pod::Spec.new do |s|
   s.name               = "SocketRocket"
-  s.version            = '0.3.1-beta2'
+  s.version            = '0.4.1'
   s.summary            = 'A conforming WebSocket (RFC 6455) client library.'
   s.homepage           = 'https://github.com/square/SocketRocket'
   s.authors            = 'Square'
   s.license            = 'Apache License, Version 2.0'
-  s.source             = { :git => 'https://github.com/square/SocketRocket.git', :commit => '82c9f8938f8b9b7aa578866cb7ce56bc11e52ced' }
-  s.source_files       = 'SocketRocket/*.{h,m,c}'
+  s.source             = { :git => 'https://github.com/square/SocketRocket.git', :tag => s.version.to_s }
+  s.source_files       = 'SocketRocket/*.{h,m}'
   s.requires_arc       = true
-  s.ios.frameworks     = %w{CFNetwork Security}
-  s.osx.frameworks     = %w{CoreServices Security}
-  s.osx.compiler_flags = '-Wno-format'
+
+  s.ios.deployment_target = '6.0'
+  s.osx.deployment_target = '10.8'
+
+  s.ios.frameworks     = 'CFNetwork', 'Security'
+  s.osx.frameworks     = 'CoreServices', 'Security'
+
   s.libraries          = "icucore"
 end

+ 34 - 70
SocketRocket.xcodeproj/project.pbxproj

@@ -7,8 +7,6 @@
 	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 */; };
 		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 */; };
@@ -28,24 +26,19 @@
 		F624180414D53449003CE997 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
 		F624180614D53451003CE997 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
 		F6396B86153E67EC00345B5E /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; };
-		F6396B87153E67EC00345B5E /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = F6016C7B146124B20037BB3D /* base64.c */; };
-		F6396B88153E67EC00345B5E /* NSData+SRB64Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */; };
-		F6572126146C7B6A00D6B8A9 /* NSData+SRB64Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */; };
 		F668C899153E923C0044DBAC /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6396BA4153E6D7400345B5E /* CoreServices.framework */; };
 		F668C89A153E923C0044DBAC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6396BA1153E6D4800345B5E /* Foundation.framework */; };
 		F668C89B153E923C0044DBAC /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6396B9F153E6D3700345B5E /* Security.framework */; };
 		F668C8AA153E92F90044DBAC /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; };
+		F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		F6A12CD2145119B700C1D980 /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; };
 		F6AE451D145906A70022AF3C /* libSocketRocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B2082D1450F597009315AF /* libSocketRocket.a */; };
 		F6AE4520145906B20022AF3C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.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 */; };
-		F6BDA804145900D200FE3253 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6BDA803145900D200FE3253 /* SenTestingKit.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 */; };
 /* End PBXBuildFile section */
 
@@ -60,8 +53,6 @@
 /* End PBXContainerItemProxy 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>"; };
 		F60CC29F14D4EA0500A005E4 /* SRTWebSocketOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRTWebSocketOperation.h; sourceTree = "<group>"; };
 		F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SRTWebSocketOperation.m; sourceTree = "<group>"; };
 		F61A0DC71625F44D00365EBD /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "Default-568h@2x.png"; path = "en.lproj/Default-568h@2x.png"; sourceTree = "<group>"; };
@@ -82,8 +73,6 @@
 		F6396B9F153E6D3700345B5E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Security.framework; sourceTree = DEVELOPER_DIR; };
 		F6396BA1153E6D4800345B5E /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
 		F6396BA4153E6D7400345B5E /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.7.sdk/System/Library/Frameworks/CoreServices.framework; sourceTree = DEVELOPER_DIR; };
-		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>"; };
 		F668C880153E91210044DBAC /* SocketRocket.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SocketRocket.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		F668C884153E91210044DBAC /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
 		F668C885153E91210044DBAC /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
@@ -98,8 +87,7 @@
 		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>"; };
-		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; };
+		F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SRWebSocketTests.xctest; sourceTree = BUILT_PRODUCTS_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>"; };
@@ -136,7 +124,6 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				F6C41C96145F7C4700641356 /* libicucore.dylib in Frameworks */,
 				F6AE4520145906B20022AF3C /* Foundation.framework in Frameworks */,
 				F6016C8914620ECC0037BB3D /* Security.framework in Frameworks */,
 				F6016C8A1462143C0037BB3D /* CFNetwork.framework in Frameworks */,
@@ -148,7 +135,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				F6C41C98145F7C6100641356 /* libicucore.dylib in Frameworks */,
-				F6BDA804145900D200FE3253 /* SenTestingKit.framework in Frameworks */,
 				F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */,
 				F6AE451D145906A70022AF3C /* libSocketRocket.a in Frameworks */,
 				F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */,
@@ -232,7 +218,7 @@
 			isa = PBXGroup;
 			children = (
 				F6B2082D1450F597009315AF /* libSocketRocket.a */,
-				F6BDA802145900D200FE3253 /* SRWebSocketTests.octest */,
+				F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */,
 				F62417E314D52F3C003CE997 /* TestChat.app */,
 				F668C880153E91210044DBAC /* SocketRocket.framework */,
 			);
@@ -247,7 +233,6 @@
 				F6A12CD51451231B00C1D980 /* CFNetwork.framework */,
 				F6A12CD3145122FC00C1D980 /* Security.framework */,
 				F6B208301450F597009315AF /* Foundation.framework */,
-				F6BDA803145900D200FE3253 /* SenTestingKit.framework */,
 				F62417E514D52F3C003CE997 /* UIKit.framework */,
 				F62417E814D52F3C003CE997 /* CoreGraphics.framework */,
 				F668C883153E91210044DBAC /* Other Frameworks */,
@@ -259,12 +244,8 @@
 			isa = PBXGroup;
 			children = (
 				F6B208331450F597009315AF /* Supporting Files */,
-				F6016C7B146124B20037BB3D /* base64.c */,
-				F6016C7E146124ED0037BB3D /* base64.h */,
 				F6A12CCF145119B700C1D980 /* SRWebSocket.h */,
 				F6A12CD0145119B700C1D980 /* SRWebSocket.m */,
-				F6572123146C7B6A00D6B8A9 /* NSData+SRB64Additions.h */,
-				F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */,
 			);
 			path = SocketRocket;
 			sourceTree = "<group>";
@@ -316,7 +297,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */,
-				F6016C7F146124ED0037BB3D /* base64.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -382,7 +362,6 @@
 				F6BDA7FD145900D200FE3253 /* Sources */,
 				F6BDA7FE145900D200FE3253 /* Frameworks */,
 				F6BDA7FF145900D200FE3253 /* Resources */,
-				F6BDA800145900D200FE3253 /* ShellScript */,
 			);
 			buildRules = (
 			);
@@ -391,8 +370,8 @@
 			);
 			name = SRWebSocketTests;
 			productName = SRWebSocketTests;
-			productReference = F6BDA802145900D200FE3253 /* SRWebSocketTests.octest */;
-			productType = "com.apple.product-type.bundle";
+			productReference = F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
 		};
 /* End PBXNativeTarget section */
 
@@ -400,7 +379,8 @@
 		F6B208241450F597009315AF /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 0500;
+				LastTestingUpgradeCheck = 0640;
+				LastUpgradeCheck = 0640;
 			};
 			buildConfigurationList = F6B208271450F597009315AF /* Build configuration list for PBXProject "SocketRocket" */;
 			compatibilityVersion = "Xcode 3.2";
@@ -450,22 +430,6 @@
 		};
 /* 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 */
 		F62417DF14D52F3C003CE997 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
@@ -483,8 +447,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				F6396B86153E67EC00345B5E /* SRWebSocket.m in Sources */,
-				F6396B87153E67EC00345B5E /* base64.c in Sources */,
-				F6396B88153E67EC00345B5E /* NSData+SRB64Additions.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -493,8 +455,6 @@
 			buildActionMask = 2147483647;
 			files = (
 				F6A12CD2145119B700C1D980 /* SRWebSocket.m in Sources */,
-				F6016C7C146124B20037BB3D /* base64.c in Sources */,
-				F6572126146C7B6A00D6B8A9 /* NSData+SRB64Additions.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -602,6 +562,7 @@
 				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
 				INFOPLIST_FILE = "SocketRocketOSX/SocketRocketOSX-Info.plist";
 				LD_DYLIB_INSTALL_NAME = "@executable_path/../Frameworks/$(EXECUTABLE_PATH)";
+				MACOSX_DEPLOYMENT_TARGET = 10.7;
 				ONLY_ACTIVE_ARCH = YES;
 				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_NAME = SocketRocket;
@@ -628,6 +589,7 @@
 				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
 				INFOPLIST_FILE = "SocketRocketOSX/SocketRocketOSX-Info.plist";
 				LD_DYLIB_INSTALL_NAME = "@executable_path/../Frameworks/$(EXECUTABLE_PATH)";
+				MACOSX_DEPLOYMENT_TARGET = 10.7;
 				OTHER_LDFLAGS = "-ObjC";
 				PRODUCT_NAME = SocketRocket;
 				SDKROOT = macosx;
@@ -646,15 +608,20 @@
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
 				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_OPTIMIZATION_LEVEL = 0;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					"DEBUG=1",
 					"$(inherited)",
 				);
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
@@ -664,7 +631,7 @@
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 5.1;
-				ONLY_ACTIVE_ARCH = NO;
+				ONLY_ACTIVE_ARCH = YES;
 				RUN_CLANG_STATIC_ANALYZER = YES;
 				SDKROOT = iphoneos;
 			};
@@ -681,14 +648,19 @@
 				CLANG_WARN_EMPTY_BODY = YES;
 				CLANG_WARN_ENUM_CONVERSION = YES;
 				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
 				COPY_PHASE_STRIP = YES;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_NO_COMMON_BLOCKS = YES;
 				GCC_PREPROCESSOR_DEFINITIONS = (
 					NDEBUG,
 					"$(inherited)",
 				);
 				GCC_SYMBOLS_PRIVATE_EXTERN = YES;
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
 				GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
 				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
 				GCC_WARN_ABOUT_RETURN_TYPE = YES;
@@ -713,11 +685,12 @@
 				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\"",
+					"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib/system\"",
+					"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib\"",
 				);
-				OTHER_LDFLAGS = "-ObjC";
+				OTHER_LDFLAGS = "-Licucore";
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PUBLIC_HEADERS_FOLDER_PATH = include/$PRODUCT_NAME;
 				SKIP_INSTALL = YES;
 			};
 			name = Debug;
@@ -731,11 +704,12 @@
 				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\"",
+					"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib/system\"",
+					"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib\"",
 				);
-				OTHER_LDFLAGS = "-ObjC";
+				OTHER_LDFLAGS = "-Licucore";
 				PRODUCT_NAME = "$(TARGET_NAME)";
+				PUBLIC_HEADERS_FOLDER_PATH = include/$PRODUCT_NAME;
 				SKIP_INSTALL = YES;
 			};
 			name = Release;
@@ -745,23 +719,18 @@
 			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\"",
+					"$(inherited)",
 				);
 				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\"",
-				);
 				OTHER_LDFLAGS = (
 					"-all_load",
 					"-ObjC",
+					"-framework",
+					XCTest,
 				);
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				WRAPPER_EXTENSION = octest;
 			};
 			name = Debug;
 		};
@@ -770,23 +739,18 @@
 			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\"",
+					"$(inherited)",
 				);
 				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\"",
-				);
 				OTHER_LDFLAGS = (
 					"-all_load",
 					"-ObjC",
+					"-framework",
+					XCTest,
 				);
 				PRODUCT_NAME = "$(TARGET_NAME)";
-				WRAPPER_EXTENSION = octest;
 			};
 			name = Release;
 		};

+ 18 - 9
SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocket.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0500"
+   LastUpgradeVersion = "0630"
    version = "1.7">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -29,7 +29,7 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "F6BDA801145900D200FE3253"
-               BuildableName = "SRWebSocketTests.octest"
+               BuildableName = "SRWebSocketTests.xctest"
                BlueprintName = "SRWebSocketTests"
                ReferencedContainer = "container:SocketRocket.xcodeproj">
             </BuildableReference>
@@ -52,7 +52,7 @@
                   <BuildableReference
                      BuildableIdentifier = "primary"
                      BlueprintIdentifier = "F6BDA801145900D200FE3253"
-                     BuildableName = "SRWebSocketTests.octest"
+                     BuildableName = "SRWebSocketTests.xctest"
                      BlueprintName = "SRWebSocketTests"
                      ReferencedContainer = "container:SocketRocket.xcodeproj">
                   </BuildableReference>
@@ -71,7 +71,7 @@
                   <BuildableReference
                      BuildableIdentifier = "primary"
                      BlueprintIdentifier = "F6BDA801145900D200FE3253"
-                     BuildableName = "SRWebSocketTests.octest"
+                     BuildableName = "SRWebSocketTests.xctest"
                      BlueprintName = "SRWebSocketTests"
                      ReferencedContainer = "container:SocketRocket.xcodeproj">
                   </BuildableReference>
@@ -85,7 +85,7 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "F6BDA801145900D200FE3253"
-               BuildableName = "SRWebSocketTests.octest"
+               BuildableName = "SRWebSocketTests.xctest"
                BlueprintName = "SRWebSocketTests"
                ReferencedContainer = "container:SocketRocket.xcodeproj">
             </BuildableReference>
@@ -117,10 +117,19 @@
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F6B2082C1450F597009315AF"
+            BuildableName = "libSocketRocket.a"
+            BlueprintName = "SocketRocket"
+            ReferencedContainer = "container:SocketRocket.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
       <AdditionalOptions>
          <AdditionalOption
-            key = "MallocScribble"
-            value = ""
+            key = "NSZombieEnabled"
+            value = "YES"
             isEnabled = "YES">
          </AdditionalOption>
          <AdditionalOption
@@ -134,8 +143,8 @@
             isEnabled = "YES">
          </AdditionalOption>
          <AdditionalOption
-            key = "NSZombieEnabled"
-            value = "YES"
+            key = "MallocScribble"
+            value = ""
             isEnabled = "YES">
          </AdditionalOption>
       </AdditionalOptions>

+ 10 - 1
SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocketOSX.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0500"
+   LastUpgradeVersion = "0630"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -39,6 +39,15 @@
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F668C87F153E91210044DBAC"
+            BuildableName = "SocketRocket.framework"
+            BlueprintName = "SocketRocketOSX"
+            ReferencedContainer = "container:SocketRocket.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
       <AdditionalOptions>
       </AdditionalOptions>
    </LaunchAction>

+ 16 - 7
SocketRocket.xcodeproj/xcshareddata/xcschemes/SocketRocketTests.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0500"
+   LastUpgradeVersion = "0630"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -15,7 +15,7 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "F6BDA801145900D200FE3253"
-               BuildableName = "SRWebSocketTests.octest"
+               BuildableName = "SRWebSocketTests.xctest"
                BlueprintName = "SRWebSocketTests"
                ReferencedContainer = "container:SocketRocket.xcodeproj">
             </BuildableReference>
@@ -33,7 +33,7 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "F6BDA801145900D200FE3253"
-               BuildableName = "SRWebSocketTests.octest"
+               BuildableName = "SRWebSocketTests.xctest"
                BlueprintName = "SRWebSocketTests"
                ReferencedContainer = "container:SocketRocket.xcodeproj">
             </BuildableReference>
@@ -49,10 +49,19 @@
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "F6BDA801145900D200FE3253"
+            BuildableName = "SRWebSocketTests.xctest"
+            BlueprintName = "SRWebSocketTests"
+            ReferencedContainer = "container:SocketRocket.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
       <AdditionalOptions>
          <AdditionalOption
-            key = "MallocScribble"
-            value = ""
+            key = "NSZombieEnabled"
+            value = "YES"
             isEnabled = "YES">
          </AdditionalOption>
          <AdditionalOption
@@ -66,8 +75,8 @@
             isEnabled = "YES">
          </AdditionalOption>
          <AdditionalOption
-            key = "NSZombieEnabled"
-            value = "YES"
+            key = "MallocScribble"
+            value = ""
             isEnabled = "YES">
          </AdditionalOption>
       </AdditionalOptions>

+ 9 - 7
SocketRocket.xcodeproj/xcshareddata/xcschemes/TestChat.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "0500"
+   LastUpgradeVersion = "0630"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
@@ -48,7 +48,8 @@
       ignoresPersistentStateOnLaunch = "NO"
       debugDocumentVersioning = "YES"
       allowLocationSimulation = "YES">
-      <BuildableProductRunnable>
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "F62417E214D52F3C003CE997"
@@ -59,8 +60,8 @@
       </BuildableProductRunnable>
       <AdditionalOptions>
          <AdditionalOption
-            key = "MallocScribble"
-            value = ""
+            key = "NSZombieEnabled"
+            value = "YES"
             isEnabled = "YES">
          </AdditionalOption>
          <AdditionalOption
@@ -74,8 +75,8 @@
             isEnabled = "YES">
          </AdditionalOption>
          <AdditionalOption
-            key = "NSZombieEnabled"
-            value = "YES"
+            key = "MallocScribble"
+            value = ""
             isEnabled = "YES">
          </AdditionalOption>
       </AdditionalOptions>
@@ -86,7 +87,8 @@
       useCustomWorkingDirectory = "NO"
       buildConfiguration = "Release"
       debugDocumentVersioning = "YES">
-      <BuildableProductRunnable>
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "F62417E214D52F3C003CE997"

+ 20 - 0
SocketRocket/Makefile

@@ -0,0 +1,20 @@
+BINS := SocketRocket.framework libSocketRocket.a
+
+all: $(BINS)
+
+HEADERS := SRWebSocket.h
+SRCS := SRWebSocket.m
+OBJS := $(SRCS:%.m=%.o)
+
+CFLAGS += -fobjc-arc
+
+libSocketRocket.a: $(OBJS)
+	$(AR) -rc $(AFLAGS) $@ $^
+
+SocketRocket.framework: libSocketRocket.a
+	mkdir -p $@/Headers
+	cp -f $(HEADERS) $@/Headers
+	cp $^ $@/SocketRocket
+
+clean:
+	rm -r $(OBJS) $(BINS)

+ 0 - 24
SocketRocket/NSData+SRB64Additions.h

@@ -1,24 +0,0 @@
-//
-//   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

+ 0 - 39
SocketRocket/NSData+SRB64Additions.m

@@ -1,39 +0,0 @@
-//
-//   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

+ 25 - 3
SocketRocket/SRWebSocket.h

@@ -17,16 +17,30 @@
 #import <Foundation/Foundation.h>
 #import <Security/SecCertificate.h>
 
-typedef enum {
+typedef NS_ENUM(NSInteger, SRReadyState) {
     SR_CONNECTING   = 0,
     SR_OPEN         = 1,
     SR_CLOSING      = 2,
     SR_CLOSED       = 3,
-} SRReadyState;
+};
+
+typedef enum SRStatusCode : NSInteger {
+    SRStatusCodeNormal = 1000,
+    SRStatusCodeGoingAway = 1001,
+    SRStatusCodeProtocolError = 1002,
+    SRStatusCodeUnhandledType = 1003,
+    // 1004 reserved.
+    SRStatusNoStatusReceived = 1005,
+    // 1004-1006 reserved.
+    SRStatusCodeInvalidUTF8 = 1007,
+    SRStatusCodePolicyViolated = 1008,
+    SRStatusCodeMessageTooBig = 1009,
+} SRStatusCode;
 
 @class SRWebSocket;
 
 extern NSString *const SRWebSocketErrorDomain;
+extern NSString *const SRHTTPResponseErrorKey;
 
 #pragma mark - SRWebSocketDelegate
 
@@ -36,7 +50,7 @@ extern NSString *const SRWebSocketErrorDomain;
 
 @interface SRWebSocket : NSObject <NSStreamDelegate>
 
-@property (nonatomic, assign) id <SRWebSocketDelegate> delegate;
+@property (nonatomic, weak) id <SRWebSocketDelegate> delegate;
 
 @property (nonatomic, readonly) SRReadyState readyState;
 @property (nonatomic, readonly, retain) NSURL *url;
@@ -46,6 +60,10 @@ extern NSString *const SRWebSocketErrorDomain;
 // For DEBUG builds this flag is ignored, and SSL connections are allowed regardless
 // of the certificate trust configuration
 @property (nonatomic, readwrite) BOOL allowUntrustedSSLCertificates;
+@property (nonatomic, readonly) CFHTTPMessageRef receivedHTTPHeaders;
+
+// Optional array of cookies (NSHTTPCookie objects) to apply to the connections
+@property (nonatomic, readwrite) NSArray * requestCookies;
 
 // This returns the negotiated protocol.
 // It will be nil until after the handshake completes.
@@ -77,6 +95,9 @@ extern NSString *const SRWebSocketErrorDomain;
 // Send a UTF8 String or Data.
 - (void)send:(id)data;
 
+// Send Data (can be nil) in a ping message.
+- (void)sendPing:(NSData *)data;
+
 @end
 
 #pragma mark - SRWebSocketDelegate
@@ -92,6 +113,7 @@ extern NSString *const SRWebSocketErrorDomain;
 - (void)webSocketDidOpen:(SRWebSocket *)webSocket;
 - (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error;
 - (void)webSocket:(SRWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean;
+- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
 
 @end
 

+ 109 - 100
SocketRocket/SRWebSocket.m

@@ -34,9 +34,6 @@
 #import <CommonCrypto/CommonDigest.h>
 #import <Security/SecRandom.h>
 
-#import "base64.h"
-#import "NSData+SRB64Additions.h"
-
 #if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
 #define sr_dispatch_retain(x)
 #define sr_dispatch_release(x)
@@ -48,7 +45,7 @@
 #endif
 
 #if !__has_feature(objc_arc) 
-#error SocketRocket muust be compiled with ARC enabled
+#error SocketRocket must be compiled with ARC enabled
 #endif
 
 
@@ -62,19 +59,6 @@ typedef enum  {
     // B-F reserved.
 } SROpCode;
 
-typedef enum {
-    SRStatusCodeNormal = 1000,
-    SRStatusCodeGoingAway = 1001,
-    SRStatusCodeProtocolError = 1002,
-    SRStatusCodeUnhandledType = 1003,
-    // 1004 reserved.
-    SRStatusNoStatusReceived = 1005,
-    // 1004-1006 reserved.
-    SRStatusCodeInvalidUTF8 = 1007,
-    SRStatusCodePolicyViolated = 1008,
-    SRStatusCodeMessageTooBig = 1009,
-} SRStatusCode;
-
 typedef struct {
     BOOL fin;
 //  BOOL rsv1;
@@ -88,7 +72,6 @@ typedef struct {
 static NSString *const SRWebSocketAppendToSecKeyString = @"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 
 static inline int32_t validate_dispatch_data_partial_string(NSData *data);
-static inline dispatch_queue_t log_queue();
 static inline void SRFastLog(NSString *format, ...);
 
 @interface NSData (SRWebSocket)
@@ -123,20 +106,21 @@ static inline void SRFastLog(NSString *format, ...);
 
 static NSString *newSHA1String(const char *bytes, size_t length) {
     uint8_t md[CC_SHA1_DIGEST_LENGTH];
+
+    assert(length >= 0);
+    assert(length <= UINT32_MAX);
+    CC_SHA1(bytes, (CC_LONG)length, md);
     
-    CC_SHA1(bytes, length, md);
-    
-    size_t buffer_size = ((sizeof(md) * 3 + 2) / 2);
-    
-    char *buffer =  (char *)malloc(buffer_size);
+    NSData *data = [NSData dataWithBytes:md length:CC_SHA1_DIGEST_LENGTH];
     
-    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];
+    if ([data respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
+        return [data base64EncodedStringWithOptions:0];
     }
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+    return [data base64Encoding];
+#pragma clang diagnostic pop
 }
 
 @implementation NSData (SRWebSocket)
@@ -159,6 +143,7 @@ static NSString *newSHA1String(const char *bytes, size_t length) {
 @end
 
 NSString *const SRWebSocketErrorDomain = @"SRWebSocketErrorDomain";
+NSString *const SRHTTPResponseErrorKey = @"HTTPResponseStatusCode";
 
 // Returns number of bytes consumed. Returning 0 means you didn't match.
 // Sends bytes to callback handler;
@@ -193,33 +178,6 @@ typedef void (^data_callback)(SRWebSocket *webSocket,  NSData *data);
 
 @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;
-
-- (void)_sendFrameWithOpcode:(SROpCode)opcode data:(id)data;
-
-- (BOOL)_checkHandshake:(CFHTTPMessageRef)httpMessage;
-- (void)_SR_commonInit;
-
-- (void)_initializeStreams;
-- (void)_connect;
-
 @property (nonatomic) SRReadyState readyState;
 
 @property (nonatomic) NSOperationQueue *delegateOperationQueue;
@@ -269,7 +227,7 @@ typedef void (^data_callback)(SRWebSocket *webSocket,  NSData *data);
     BOOL _secure;
     NSURLRequest *_urlRequest;
 
-    CFHTTPMessageRef _receivedHTTPHeaders;
+    
     
     BOOL _sentClose;
     BOOL _didFail;
@@ -381,8 +339,10 @@ static __strong NSData *CRLFCRLF;
     [_inputStream close];
     [_outputStream close];
     
-    sr_dispatch_release(_workQueue);
-    _workQueue = NULL;
+    if (_workQueue) {
+        sr_dispatch_release(_workQueue);
+        _workQueue = NULL;
+    }
     
     if (_receivedHTTPHeaders) {
         CFRelease(_receivedHTTPHeaders);
@@ -414,7 +374,7 @@ static __strong NSData *CRLFCRLF;
 
     _selfRetain = self;
     
-    [self _connect];
+    [self openConnection];
 }
 
 // Calls block on delegate queue
@@ -461,9 +421,8 @@ static __strong NSData *CRLFCRLF;
     
     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 %ld", (long)responseCode] forKey:NSLocalizedDescriptionKey]]];
+        [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2132 userInfo:@{NSLocalizedDescriptionKey:[NSString stringWithFormat:@"received bad response code from server %ld", (long)responseCode], SRHTTPResponseErrorKey:@(responseCode)}]];
         return;
-
     }
     
     if(![self _checkHandshake:_receivedHTTPHeaders]) {
@@ -524,9 +483,27 @@ static __strong NSData *CRLFCRLF;
         
     NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
     SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);
-    _secKey = [keyBytes SR_stringByBase64Encoding];
-    assert([_secKey length] == 24);
     
+    if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
+        _secKey = [keyBytes base64EncodedStringWithOptions:0];
+    } else {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+        _secKey = [keyBytes base64Encoding];
+#pragma clang diagnostic pop
+    }
+    
+    assert([_secKey length] == 24);
+
+    // Apply cookies if any have been provided
+    NSDictionary * cookies = [NSHTTPCookie requestHeaderFieldsWithCookies:[self requestCookies]];
+    for (NSString * cookieKey in cookies) {
+        NSString * cookieValue = [cookies objectForKey:cookieKey];
+        if ([cookieKey length] && [cookieValue length]) {
+            CFHTTPMessageSetHeaderFieldValue(request, (__bridge CFStringRef)cookieKey, (__bridge CFStringRef)cookieValue);
+        }
+    }
+ 
     CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Upgrade"), CFSTR("websocket"));
     CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Connection"), CFSTR("Upgrade"));
     CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Sec-WebSocket-Key"), (__bridge CFStringRef)_secKey);
@@ -552,7 +529,8 @@ static __strong NSData *CRLFCRLF;
 
 - (void)_initializeStreams;
 {
-    NSInteger port = _url.port.integerValue;
+    assert(_url.port.unsignedIntValue <= UINT32_MAX);
+    uint32_t port = _url.port.unsignedIntValue;
     if (port == 0) {
         if (!_secure) {
             port = 80;
@@ -602,7 +580,7 @@ static __strong NSData *CRLFCRLF;
     }
 }
 
-- (void)_connect;
+- (void)openConnection;
 {
     [self _updateSecureStreamOptions];
     
@@ -633,7 +611,7 @@ static __strong NSData *CRLFCRLF;
 
 - (void)close;
 {
-    [self closeWithCode:-1 reason:nil];
+    [self closeWithCode:SRStatusCodeNormal reason:nil];
 }
 
 - (void)closeWithCode:(NSInteger)code reason:(NSString *)reason;
@@ -651,7 +629,7 @@ static __strong NSData *CRLFCRLF;
         SRFastLog(@"Closing with code %d reason %@", code, reason);
         
         if (wasConnecting) {
-            [self _disconnect];
+            [self closeConnection];
             return;
         }
 
@@ -667,6 +645,7 @@ static __strong NSData *CRLFCRLF;
             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];
+            #pragma unused (success)
             
             assert(success);
             assert(remainingRange.length == 0);
@@ -687,7 +666,7 @@ static __strong NSData *CRLFCRLF;
     [self _performDelegateBlock:^{
         [self closeWithCode:SRStatusCodeProtocolError reason:message];
         dispatch_async(_workQueue, ^{
-            [self _disconnect];
+            [self closeConnection];
         });
     }];
 }
@@ -708,7 +687,7 @@ static __strong NSData *CRLFCRLF;
 
             SRFastLog(@"Failing with error %@", error.localizedDescription);
             
-            [self _disconnect];
+            [self closeConnection];
         }
     });
 }
@@ -723,6 +702,7 @@ static __strong NSData *CRLFCRLF;
     [_outputBuffer appendData:data];
     [self _pumpWriting];
 }
+
 - (void)send:(id)data;
 {
     NSAssert(self.readyState != SR_CONNECTING, @"Invalid State: Cannot call send: until connection is open");
@@ -741,6 +721,16 @@ static __strong NSData *CRLFCRLF;
     });
 }
 
+- (void)sendPing:(NSData *)data;
+{
+    NSAssert(self.readyState == SR_OPEN, @"Invalid State: Cannot call send: until connection is open");
+    // TODO: maybe not copy this for performance
+    data = [data copy] ?: [NSData data]; // It's okay for a ping to be empty
+    dispatch_async(_workQueue, ^{
+        [self _sendFrameWithOpcode:SROpCodePing data:data];
+    });
+}
+
 - (void)handlePing:(NSData *)pingData;
 {
     // Need to pingpong this off _callbackQueue first to make sure messages happen in order
@@ -751,9 +741,14 @@ static __strong NSData *CRLFCRLF;
     }];
 }
 
-- (void)handlePong;
+- (void)handlePong:(NSData *)pongData;
 {
-    // NOOP
+    SRFastLog(@"Received pong");
+    [self _performDelegateBlock:^{
+        if ([self.delegate respondsToSelector:@selector(webSocket:didReceivePong:)]) {
+            [self.delegate webSocket:self didReceivePong:pongData];
+        }
+    }];
 }
 
 - (void)_handleMessage:(id)message
@@ -834,11 +829,11 @@ static inline BOOL closeCodeIsValid(int closeCode) {
         [self closeWithCode:1000 reason:nil];
     }
     dispatch_async(_workQueue, ^{
-        [self _disconnect];
+        [self closeConnection];
     });
 }
 
-- (void)_disconnect;
+- (void)closeConnection;
 {
     [self assertOnWorkQueue];
     SRFastLog(@"Trying to disconnect");
@@ -865,7 +860,7 @@ static inline BOOL closeCodeIsValid(int closeCode) {
             if (str == nil && frameData) {
                 [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"];
                 dispatch_async(_workQueue, ^{
-                    [self _disconnect];
+                    [self closeConnection];
                 });
 
                 return;
@@ -883,7 +878,7 @@ static inline BOOL closeCodeIsValid(int closeCode) {
             [self handlePing:frameData];
             break;
         case SROpCodePong:
-            [self handlePong];
+            [self handlePong:frameData];
             break;
         default:
             [self _closeWithProtocolError:[NSString stringWithFormat:@"Unknown opcode %ld", (long)opcode]];
@@ -930,7 +925,8 @@ static inline BOOL closeCodeIsValid(int closeCode) {
             }
         }
     } else {
-        [self _addConsumerWithDataLength:frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) {
+        assert(frame_header.payload_length <= SIZE_T_MAX);
+        [self _addConsumerWithDataLength:(size_t)frame_header.payload_length callback:^(SRWebSocket *self, NSData *newData) {
             if (isControlFrame) {
                 [self _handleFrameWithData:newData opCode:frame_header.opcode];
             } else {
@@ -1031,6 +1027,7 @@ static const uint8_t SRPayloadLenMask   = 0x7F;
         } else {
             [self _addConsumerWithDataLength:extra_bytes_needed callback:^(SRWebSocket *self, NSData *data) {
                 size_t mapped_size = data.length;
+                #pragma unused (mapped_size)
                 const void *mapped_buffer = data.bytes;
                 size_t offset = 0;
                 
@@ -1047,7 +1044,6 @@ static const uint8_t SRPayloadLenMask   = 0x7F;
                     assert(header.payload_length < 126 && header.payload_length >= 0);
                 }
                 
-                
                 if (header.masked) {
                     assert(mapped_size >= sizeof(_currentReadMaskOffset) + offset);
                     memcpy(self->_currentReadMaskKey, ((uint8_t *)mapped_buffer) + offset, sizeof(self->_currentReadMaskKey));
@@ -1081,7 +1077,7 @@ static const uint8_t SRPayloadLenMask   = 0x7F;
     if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) {
         NSInteger 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]]];
+            [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]];
              return;
         }
         
@@ -1255,7 +1251,7 @@ static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
                     if (valid_utf8_size == -1) {
                         [self closeWithCode:SRStatusCodeInvalidUTF8 reason:@"Text frames must be valid UTF-8"];
                         dispatch_async(_workQueue, ^{
-                            [self _disconnect];
+                            [self closeConnection];
                         });
                         return didWork;
                     } else {
@@ -1308,7 +1304,11 @@ static const size_t SRFrameHeaderOverhead = 32;
 {
     [self assertOnWorkQueue];
     
-    NSAssert(data == nil || [data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"Function expects nil, NSString or NSData");
+    if (nil == data) {
+        return;
+    }
+    
+    NSAssert([data isKindOfClass:[NSData class]] || [data isKindOfClass:[NSString class]], @"NSString or NSData");
     
     size_t payloadLength = [data isKindOfClass:[NSString class]] ? [(NSString *)data lengthOfBytesUsingEncoding:NSUTF8StringEncoding] : [data length];
         
@@ -1340,7 +1340,7 @@ static const size_t SRFrameHeaderOverhead = 32;
     } else if ([data isKindOfClass:[NSString class]]) {
         unmasked_payload =  (const uint8_t *)[data UTF8String];
     } else {
-        assert(NO);
+        return;
     }
     
     if (payloadLength < 126) {
@@ -1405,7 +1405,7 @@ static const size_t SRFrameHeaderOverhead = 32;
             
             if (!_pinnedCertFound) {
                 dispatch_async(_workQueue, ^{
-                    [self _failWithError:[NSError errorWithDomain:@"org.lolrus.SocketRocket" code:23556 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid server cert"] forKey:NSLocalizedDescriptionKey]]];
+                    [self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:23556 userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"Invalid server cert"] forKey:NSLocalizedDescriptionKey]]];
                 });
                 return;
             }
@@ -1455,7 +1455,7 @@ static const size_t SRFrameHeaderOverhead = 32;
                         // If we get closed in this state it's probably not clean because we should be sending this when we send messages
                         [self _performDelegateBlock:^{
                             if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
-                                [self.delegate webSocket:self didCloseWithCode:0 reason:@"Stream end encountered" wasClean:NO];
+                                [self.delegate webSocket:self didCloseWithCode:SRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO];
                             }
                         }];
                     }
@@ -1470,7 +1470,7 @@ static const size_t SRFrameHeaderOverhead = 32;
                 uint8_t buffer[bufferSize];
                 
                 while (_inputStream.hasBytesAvailable) {
-                    int bytes_read = [_inputStream read:buffer maxLength:bufferSize];
+                    NSInteger bytes_read = [_inputStream read:buffer maxLength:bufferSize];
                     
                     if (bytes_read > 0) {
                         [_readBuffer appendBytes:buffer length:bytes_read];
@@ -1613,16 +1613,6 @@ static const size_t SRFrameHeaderOverhead = 32;
 
 @end
 
-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;
-}
-
 //#define SR_ENABLE_LOG
 
 static inline void SRFastLog(NSString *format, ...)  {
@@ -1642,9 +1632,14 @@ static inline void SRFastLog(NSString *format, ...)  {
 #ifdef HAS_ICU
 
 static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
+    if ([data length] > INT32_MAX) {
+        // INT32_MAX is the limit so long as this Framework is using 32 bit ints everywhere.
+        return -1;
+    }
+
+    int32_t size = (int32_t)[data length];
+
     const void * contents = [data bytes];
-    long size = [data length];
-    
     const uint8_t *str = (const uint8_t *)contents;
     
     UChar32 codepoint = 1;
@@ -1692,7 +1687,7 @@ static inline int32_t validate_dispatch_data_partial_string(NSData *data) {
     for (int i = 0; i < maxCodepointSize; i++) {
         NSString *str = [[NSString alloc] initWithBytesNoCopy:(char *)data.bytes length:data.length - i encoding:NSUTF8StringEncoding freeWhenDone:NO];
         if (str) {
-            return data.length - i;
+            return (int32_t)data.length - i;
         }
     }
     
@@ -1748,8 +1743,22 @@ static NSRunLoop *networkRunLoop = nil;
         _runLoop = [NSRunLoop currentRunLoop];
         dispatch_group_leave(_waitGroup);
         
-        NSTimer *timer = [[NSTimer alloc] initWithFireDate:[NSDate distantFuture] interval:0.0 target:nil selector:nil userInfo:nil repeats:NO];
-        [_runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
+        // Add an empty run loop source to prevent runloop from spinning.
+        CFRunLoopSourceContext sourceCtx = {
+            .version = 0,
+            .info = NULL,
+            .retain = NULL,
+            .release = NULL,
+            .copyDescription = NULL,
+            .equal = NULL,
+            .hash = NULL,
+            .schedule = NULL,
+            .cancel = NULL,
+            .perform = NULL
+        };
+        CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);
+        CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
+        CFRelease(source);
         
         while ([_runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]) {
             

+ 0 - 314
SocketRocket/base64.c

@@ -1,314 +0,0 @@
-/*	$OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $	*/
-
-/*
- * Copyright (c) 1996 by Internet Software Consortium.
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
- */
-
-/*
- * Portions Copyright (c) 1995 by International Business Machines, Inc.
- *
- * International Business Machines, Inc. (hereinafter called IBM) grants
- * permission under its copyrights to use, copy, modify, and distribute this
- * Software with or without fee, provided that the above copyright notice and
- * all paragraphs of this notice appear in all copies, and that the name of IBM
- * not be used in connection with the marketing of any product incorporating
- * the Software or modifications thereof, without specific, written prior
- * permission.
- *
- * To the extent it has a right to do so, IBM grants an immunity from suit
- * under its patents, if any, for the use, sale or manufacture of products to
- * the extent that such products are used for performing Domain Name System
- * dynamic updates in TCP/IP networks by means of the Software.  No immunity is
- * granted for any product per se or for any other function of any product.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
- * PARTICULAR PURPOSE.  IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
- * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
- * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
- * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
- */
-
-/* OPENBSD ORIGINAL: lib/libc/net/base64.c */
-
-
-#if (!defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP)) || (!defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON))
-
-#include <sys/types.h>
-#include <sys/param.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-
-#include <ctype.h>
-#include <stdio.h>
-
-#include <stdlib.h>
-#include <string.h>
-
-#include "base64.h"
-
-static const char Base64[] =
-"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-static const char Pad64 = '=';
-
-/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
- The following encoding technique is taken from RFC 1521 by Borenstein
- and Freed.  It is reproduced here in a slightly edited form for
- convenience.
- 
- A 65-character subset of US-ASCII is used, enabling 6 bits to be
- represented per printable character. (The extra 65th character, "=",
- is used to signify a special processing function.)
- 
- The encoding process represents 24-bit groups of input bits as output
- strings of 4 encoded characters. Proceeding from left to right, a
- 24-bit input group is formed by concatenating 3 8-bit input groups.
- These 24 bits are then treated as 4 concatenated 6-bit groups, each
- of which is translated into a single digit in the base64 alphabet.
- 
- Each 6-bit group is used as an index into an array of 64 printable
- characters. The character referenced by the index is placed in the
- output string.
- 
- Table 1: The Base64 Alphabet
- 
- Value Encoding  Value Encoding  Value Encoding  Value Encoding
- 0 A            17 R            34 i            51 z
- 1 B            18 S            35 j            52 0
- 2 C            19 T            36 k            53 1
- 3 D            20 U            37 l            54 2
- 4 E            21 V            38 m            55 3
- 5 F            22 W            39 n            56 4
- 6 G            23 X            40 o            57 5
- 7 H            24 Y            41 p            58 6
- 8 I            25 Z            42 q            59 7
- 9 J            26 a            43 r            60 8
- 10 K            27 b            44 s            61 9
- 11 L            28 c            45 t            62 +
- 12 M            29 d            46 u            63 /
- 13 N            30 e            47 v
- 14 O            31 f            48 w         (pad) =
- 15 P            32 g            49 x
- 16 Q            33 h            50 y
- 
- Special processing is performed if fewer than 24 bits are available
- at the end of the data being encoded.  A full encoding quantum is
- always completed at the end of a quantity.  When fewer than 24 input
- bits are available in an input group, zero bits are added (on the
- right) to form an integral number of 6-bit groups.  Padding at the
- end of the data is performed using the '=' character.
- 
- Since all base64 input is an integral number of octets, only the
- -------------------------------------------------                       
- following cases can arise:
- 
- (1) the final quantum of encoding input is an integral
- multiple of 24 bits; here, the final unit of encoded
- output will be an integral multiple of 4 characters
- with no "=" padding,
- (2) the final quantum of encoding input is exactly 8 bits;
- here, the final unit of encoded output will be two
- characters followed by two "=" padding characters, or
- (3) the final quantum of encoding input is exactly 16 bits;
- here, the final unit of encoded output will be three
- characters followed by one "=" padding character.
- */
-
-#if !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) 
-int
-b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize)
-{
-	size_t datalength = 0;
-	u_char input[3];
-	u_char output[4];
-	u_int i;
-    
-	while (2 < srclength) {
-		input[0] = *src++;
-		input[1] = *src++;
-		input[2] = *src++;
-		srclength -= 3;
-        
-		output[0] = input[0] >> 2;
-		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
-		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
-		output[3] = input[2] & 0x3f;
-        
-		if (datalength + 4 > targsize)
-			return (-1);
-		target[datalength++] = Base64[output[0]];
-		target[datalength++] = Base64[output[1]];
-		target[datalength++] = Base64[output[2]];
-		target[datalength++] = Base64[output[3]];
-	}
-    
-	/* Now we worry about padding. */
-	if (0 != srclength) {
-		/* Get what's left. */
-		input[0] = input[1] = input[2] = '\0';
-		for (i = 0; i < srclength; i++)
-			input[i] = *src++;
-        
-		output[0] = input[0] >> 2;
-		output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
-		output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
-        
-		if (datalength + 4 > targsize)
-			return (-1);
-		target[datalength++] = Base64[output[0]];
-		target[datalength++] = Base64[output[1]];
-		if (srclength == 1)
-			target[datalength++] = Pad64;
-		else
-			target[datalength++] = Base64[output[2]];
-		target[datalength++] = Pad64;
-	}
-	if (datalength >= targsize)
-		return (-1);
-	target[datalength] = '\0';	/* Returned value doesn't count \0. */
-	return (datalength);
-}
-#endif /* !defined(HAVE_B64_NTOP) && !defined(HAVE___B64_NTOP) */
-
-#if !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON)
-
-/* skips all whitespace anywhere.
- converts characters, four at a time, starting at (or after)
- src from base - 64 numbers into three 8 bit bytes in the target area.
- it returns the number of data bytes stored at the target, or -1 on error.
- */
-
-int
-b64_pton(char const *src, u_char *target, size_t targsize)
-{
-	u_int tarindex, state;
-	int ch;
-	char *pos;
-    
-	state = 0;
-	tarindex = 0;
-    
-	while ((ch = *src++) != '\0') {
-		if (isspace(ch))	/* Skip whitespace anywhere. */
-			continue;
-        
-		if (ch == Pad64)
-			break;
-        
-		pos = strchr(Base64, ch);
-		if (pos == 0) 		/* A non-base64 character. */
-			return (-1);
-        
-		switch (state) {
-            case 0:
-                if (target) {
-                    if (tarindex >= targsize)
-                        return (-1);
-                    target[tarindex] = (pos - Base64) << 2;
-                }
-                state = 1;
-                break;
-            case 1:
-                if (target) {
-                    if (tarindex + 1 >= targsize)
-                        return (-1);
-                    target[tarindex]   |=  (pos - Base64) >> 4;
-                    target[tarindex+1]  = ((pos - Base64) & 0x0f)
-                    << 4 ;
-                }
-                tarindex++;
-                state = 2;
-                break;
-            case 2:
-                if (target) {
-                    if (tarindex + 1 >= targsize)
-                        return (-1);
-                    target[tarindex]   |=  (pos - Base64) >> 2;
-                    target[tarindex+1]  = ((pos - Base64) & 0x03)
-                    << 6;
-                }
-                tarindex++;
-                state = 3;
-                break;
-            case 3:
-                if (target) {
-                    if (tarindex >= targsize)
-                        return (-1);
-                    target[tarindex] |= (pos - Base64);
-                }
-                tarindex++;
-                state = 0;
-                break;
-		}
-	}
-    
-	/*
-	 * We are done decoding Base-64 chars.  Let's see if we ended
-	 * on a byte boundary, and/or with erroneous trailing characters.
-	 */
-    
-	if (ch == Pad64) {		/* We got a pad char. */
-		ch = *src++;		/* Skip it, get next. */
-		switch (state) {
-            case 0:		/* Invalid = in first position */
-            case 1:		/* Invalid = in second position */
-                return (-1);
-                
-            case 2:		/* Valid, means one byte of info */
-                /* Skip any number of spaces. */
-                for (; ch != '\0'; ch = *src++)
-                    if (!isspace(ch))
-                        break;
-                /* Make sure there is another trailing = sign. */
-                if (ch != Pad64)
-                    return (-1);
-                ch = *src++;		/* Skip the = */
-                /* Fall through to "single trailing =" case. */
-                /* FALLTHROUGH */
-                
-            case 3:		/* Valid, means two bytes of info */
-                /*
-                 * We know this char is an =.  Is there anything but
-                 * whitespace after it?
-                 */
-                for (; ch != '\0'; ch = *src++)
-                    if (!isspace(ch))
-                        return (-1);
-                
-                /*
-                 * Now make sure for cases 2 and 3 that the "extra"
-                 * bits that slopped past the last full byte were
-                 * zeros.  If we don't check them, they become a
-                 * subliminal channel.
-                 */
-                if (target && target[tarindex] != 0)
-                    return (-1);
-		}
-	} else {
-		/*
-		 * We ended by seeing the end of the string.  Make sure we
-		 * have no partial bytes lying around.
-		 */
-		if (state != 0)
-			return (-1);
-	}
-    
-	return (tarindex);
-}
-
-#endif /* !defined(HAVE_B64_PTON) && !defined(HAVE___B64_PTON) */
-#endif 

+ 0 - 34
SocketRocket/base64.h

@@ -1,34 +0,0 @@
-//   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.
-//
-
-
-#ifndef SocketRocket_base64_h
-#define SocketRocket_base64_h
-
-#include <sys/types.h>
-
-extern int
-b64_ntop(u_char const *src,
-         size_t srclength,
-         char *target,
-         size_t targsize);
-
-extern int
-b64_pton(char const *src,
-         u_char *target, 
-         size_t targsize);
-
-
-#endif

+ 1 - 0
TestChat/TCViewController.h

@@ -13,5 +13,6 @@
 @property (nonatomic, retain) IBOutlet UITextView *inputView;
 
 - (IBAction)reconnect:(id)sender;
+- (IBAction)sendPing:(id)sender;
 
 @end

+ 11 - 1
TestChat/TCViewController.m

@@ -7,7 +7,7 @@
 //
 
 #import "TCViewController.h"
-#import "SRWebSocket.h"
+#import <SocketRocket/SRWebSocket.h>
 #import "TCChatCell.h"
 
 @interface TCMessage : NSObject
@@ -65,6 +65,11 @@
     [self _reconnect];
 }
 
+- (void)sendPing:(id)sender;
+{
+    [_webSocket sendPing:nil];
+}
+
 - (void)viewDidAppear:(BOOL)animated;
 {
     [super viewDidAppear:animated];
@@ -135,6 +140,11 @@
     _webSocket = nil;
 }
 
+- (void)webSocket:(SRWebSocket *)webSocket didReceivePong:(NSData *)pongPayload;
+{
+    NSLog(@"Websocket received pong");
+}
+
 - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
 {
     if ([text rangeOfString:@"\n"].location != NSNotFound) {

+ 20 - 33
TestChat/en.lproj/MainStoryboard.storyboard

@@ -1,21 +1,20 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="1.1" toolsVersion="2166" systemVersion="11D50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="J5d-9g-n8O">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5056" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" initialViewController="J5d-9g-n8O">
     <dependencies>
-        <development defaultVersion="4200" identifier="xcode"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="1166"/>
+        <deployment defaultVersion="1280" identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3733"/>
     </dependencies>
     <scenes>
         <!--View Controller-->
         <scene sceneID="6me-oX-IDw">
             <objects>
-                <placeholder placeholderIdentifier="IBFirstResponder" id="KsG-hH-48N" userLabel="First Responder" sceneMemberID="firstResponder"/>
                 <tableViewController id="X5f-jW-I9m" customClass="TCViewController" sceneMemberID="viewController">
-                    <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="RXu-lL-Fvu">
-                        <rect key="frame" x="0.0" y="64" width="320" height="416"/>
+                    <tableView key="view" opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" allowsSelection="NO" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="RXu-lL-Fvu">
+                        <rect key="frame" x="0.0" y="0.0" width="320" height="480"/>
                         <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                         <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
                         <textView key="tableFooterView" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" id="rfU-78-BK7">
-                            <rect key="frame" x="0.0" y="110" width="320" height="48"/>
+                            <rect key="frame" x="0.0" y="174" width="320" height="48"/>
                             <autoresizingMask key="autoresizingMask" widthSizable="YES"/>
                             <color key="backgroundColor" red="0.90196079015731812" green="0.90196079015731812" blue="0.90196079015731812" alpha="1" colorSpace="calibratedRGB"/>
                             <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
@@ -27,9 +26,9 @@
                         </textView>
                         <prototypes>
                             <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="SentCell" id="bz8-oG-fQA" customClass="TCChatCell">
-                                <rect key="frame" x="0.0" y="22" width="320" height="44"/>
+                                <rect key="frame" x="0.0" y="86" width="320" height="44"/>
                                 <autoresizingMask key="autoresizingMask"/>
-                                <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="bz8-oG-fQA" id="ZUj-kZ-dtZ">
                                     <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
@@ -45,17 +44,16 @@
                                             </connections>
                                         </textView>
                                     </subviews>
-                                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
-                                </view>
+                                </tableViewCellContentView>
                                 <color key="backgroundColor" red="0.40000000596046448" green="0.80000001192092896" blue="1" alpha="1" colorSpace="calibratedRGB"/>
                                 <connections>
                                     <outlet property="textView" destination="5Mf-2x-lct" id="9XD-07-EiF"/>
                                 </connections>
                             </tableViewCell>
                             <tableViewCell contentMode="scaleToFill" selectionStyle="blue" hidesAccessoryWhenEditing="NO" indentationLevel="1" indentationWidth="0.0" reuseIdentifier="ReceivedCell" id="RDH-ix-a2K" customClass="TCChatCell">
-                                <rect key="frame" x="0.0" y="66" width="320" height="44"/>
+                                <rect key="frame" x="0.0" y="130" width="320" height="44"/>
                                 <autoresizingMask key="autoresizingMask"/>
-                                <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
+                                <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="RDH-ix-a2K" id="HND-fP-cKl">
                                     <rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
                                     <autoresizingMask key="autoresizingMask"/>
                                     <subviews>
@@ -71,8 +69,7 @@
                                             </connections>
                                         </textView>
                                     </subviews>
-                                    <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
-                                </view>
+                                </tableViewCellContentView>
                                 <color key="backgroundColor" red="0.40000000600000002" green="0.80000001190000003" blue="1" alpha="1" colorSpace="calibratedRGB"/>
                                 <connections>
                                     <outlet property="textView" destination="D4G-dV-Fxa" id="uY3-zG-DIh"/>
@@ -85,6 +82,11 @@
                         </connections>
                     </tableView>
                     <navigationItem key="navigationItem" id="hu8-ml-uHB">
+                        <barButtonItem key="leftBarButtonItem" title="Ping" id="zLH-d1-wAa">
+                            <connections>
+                                <action selector="sendPing:" destination="X5f-jW-I9m" id="y6C-K5-50Q"/>
+                            </connections>
+                        </barButtonItem>
                         <barButtonItem key="rightBarButtonItem" systemItem="refresh" id="cNK-1A-b66">
                             <connections>
                                 <action selector="reconnect:" destination="X5f-jW-I9m" id="V5L-sy-yng"/>
@@ -95,13 +97,13 @@
                         <outlet property="inputView" destination="rfU-78-BK7" id="emF-S6-iR0"/>
                     </connections>
                 </tableViewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="KsG-hH-48N" userLabel="First Responder" sceneMemberID="firstResponder"/>
             </objects>
             <point key="canvasLocation" x="676" y="93"/>
         </scene>
         <!--Navigation Controller-->
         <scene sceneID="nGX-KT-vxI">
             <objects>
-                <placeholder placeholderIdentifier="IBFirstResponder" id="DyG-Ww-kME" userLabel="First Responder" sceneMemberID="firstResponder"/>
                 <navigationController id="J5d-9g-n8O" sceneMemberID="viewController">
                     <toolbarItems/>
                     <navigationBar key="navigationBar" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" id="8yP-KL-gkV">
@@ -113,29 +115,14 @@
                         <segue destination="X5f-jW-I9m" kind="relationship" relationship="rootViewController" id="3EC-DA-AY5"/>
                     </connections>
                 </navigationController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="DyG-Ww-kME" userLabel="First Responder" sceneMemberID="firstResponder"/>
             </objects>
             <point key="canvasLocation" x="227" y="65"/>
         </scene>
     </scenes>
-    <classes>
-        <class className="TCChatCell" superclassName="UITableViewCell">
-            <source key="sourceIdentifier" type="project" relativePath="./Classes/TCChatCell.h"/>
-            <relationships>
-                <relationship kind="outlet" name="nameLabel" candidateClass="UILabel"/>
-                <relationship kind="outlet" name="textView" candidateClass="UITextView"/>
-            </relationships>
-        </class>
-        <class className="TCViewController" superclassName="UITableViewController">
-            <source key="sourceIdentifier" type="project" relativePath="./Classes/TCViewController.h"/>
-            <relationships>
-                <relationship kind="action" name="reconnect:"/>
-                <relationship kind="outlet" name="inputView" candidateClass="UITextView"/>
-            </relationships>
-        </class>
-    </classes>
     <simulatedMetricsContainer key="defaultSimulatedMetrics">
         <simulatedStatusBarMetrics key="statusBar"/>
         <simulatedOrientationMetrics key="orientation"/>
         <simulatedScreenMetrics key="destination"/>
     </simulatedMetricsContainer>
-</document>
+</document>

+ 42 - 41
TestChatServer/go/chatroom.go

@@ -1,60 +1,61 @@
 package main
 
 import (
-    "code.google.com/p/go.net/websocket"
-    "net/http"
+	"code.google.com/p/go.net/websocket"
+	"net/http"
 )
 
+// Msg stores both the message and the connection
 type Msg struct {
-    sender *websocket.Conn
-    msg    string
+	sender *websocket.Conn
+	msg    string
 }
 
 func run(reg chan *websocket.Conn, unreg chan *websocket.Conn, msg chan Msg) {
-    conns := make(map[*websocket.Conn]int)
-    for {
-        select {
-        case c := <-reg:
-            conns[c] = 1
-        case c := <-unreg:
-            delete(conns, c)
-        case msg := <-msg:
-            for c := range conns {
-                if c != msg.sender {
-                    websocket.Message.Send(c, msg.msg)
-                }
-            }
-        }
-    }
+	conns := make(map[*websocket.Conn]int)
+	for {
+		select {
+		case c := <-reg:
+			conns[c] = 1
+		case c := <-unreg:
+			delete(conns, c)
+		case msg := <-msg:
+			for c := range conns {
+				if c != msg.sender {
+					websocket.Message.Send(c, msg.msg)
+				}
+			}
+		}
+	}
 }
 
 func newChatServer(reg chan *websocket.Conn, unreg chan *websocket.Conn, msg chan Msg) websocket.Handler {
-    return func(ws *websocket.Conn) {
-        reg <- ws
-        for {
-            var message string
-            err := websocket.Message.Receive(ws, &message)
-            if err != nil {
-                unreg <- ws
-                break
-            }
-            msg <- Msg{ws, message}
-        }
-    }
+	return func(ws *websocket.Conn) {
+		reg <- ws
+		for {
+			var message string
+			err := websocket.Message.Receive(ws, &message)
+			if err != nil {
+				unreg <- ws
+				break
+			}
+			msg <- Msg{ws, message}
+		}
+	}
 }
 
 func main() {
-    reg := make(chan *websocket.Conn)
-    unreg := make(chan *websocket.Conn)
-    msg := make(chan Msg)
+	reg := make(chan *websocket.Conn)
+	unreg := make(chan *websocket.Conn)
+	msg := make(chan Msg)
 
-    http.Handle("/chat", websocket.Handler(newChatServer(reg, unreg, msg)))
-    http.Handle("/", http.FileServer(http.Dir("../static")))
+	http.Handle("/chat", websocket.Handler(newChatServer(reg, unreg, msg)))
+	http.Handle("/", http.FileServer(http.Dir("../static")))
 
-    go run(reg, unreg, msg)
+	go run(reg, unreg, msg)
 
-    err := http.ListenAndServe(":9000", nil)
-    if err != nil {
-        panic("ListenAndServe: " + err.Error())
-    }
+	err := http.ListenAndServe(":9000", nil)
+	if err != nil {
+		panic("ListenAndServe: " + err.Error())
+	}
 }