Compare commits

...

28 Commits

Author SHA1 Message Date
Mike Lewis
9cefb83328 Partially converted from using promises to RxSwift 2016-01-07 19:49:53 -08:00
Mike Lewis
15caaa0cc9 Connect and listen work 2015-08-10 23:37:49 -07:00
Mike Lewis
0266b0b7f4 COnverting promises to be ordered on queues 2015-08-08 13:36:42 -07:00
Mike Lewis
acd07c14de Better 2015-08-07 18:48:28 -07:00
Mike Lewis
95de1ea335 Moar cleanup 2015-08-07 17:32:28 -07:00
Mike Lewis
e3c402beab More unicode tests 2015-08-07 17:28:00 -07:00
Mike Lewis
f616386954 Making promise better 2015-08-07 13:38:57 -07:00
Mike Lewis
61b19ab633 Adding once 2015-08-07 10:52:07 -07:00
Mike Lewis
c62bfb90ad UTF8 Handling 2015-08-06 23:07:39 -07:00
Mike Lewis
d7078b082c Error ahndling 2015-08-06 15:01:05 -07:00
Mike Lewis
49c79072d2 Making API more JS-like 2015-08-05 16:38:18 -07:00
Mike Lewis
a797a0f568 Adding tests for getaddrinfo 2015-08-05 01:40:13 -07:00
Mike Lewis
7c6e18e858 Yeah 2015-08-04 23:04:35 -07:00
Mike Lewis
e6c3850c9b Promises are great 2015-08-04 23:02:48 -07:00
Mike Lewis
fe572dc752 Better promises 2015-08-04 21:03:26 -07:00
Mike Lewis
9b0d5134f8 More promise stuff 2015-08-03 23:48:37 -07:00
Mike Lewis
1883c521d6 Adding future library 2015-08-03 23:39:00 -07:00
Mike Lewis
5214ed5347 Getting shit working 2015-07-31 18:59:32 -07:00
Mike Lewis
7ea9d25976 Adding listening 2015-07-31 18:59:12 -07:00
Mike Lewis
5278664ab7 Merge remote-tracking branch 'origin/master' into lewis/dispatch-awesome 2015-07-30 16:46:50 -07:00
Mike Lewis
2a2ed772c3 More fixes and improvements 2013-01-16 23:51:24 -08:00
Mike Lewis
d237938434 Pew pew it works... kinda. 2013-01-16 23:11:11 -08:00
Mike Lewis
ac861d6ff2 Added test harness and it seems like things are kind of working 2013-01-16 00:29:30 -08:00
Mike Lewis
2ab4b7d421 The TLS seems to work
Cleaned up GCD so OS_OBJECT_USE_OBJC_RETAIN_RELEASE is optional
2013-01-15 23:49:26 -08:00
Mike Lewis
edd6faceed Added Non-working SecureIO 2013-01-13 11:48:49 -08:00
Mike Lewis
f58b21618c WiP 2013-01-12 23:40:03 -08:00
Mike Lewis
fdf93c6f9c Async Connection 2013-01-12 19:57:18 -08:00
Mike Lewis
72f7f81ff4 Porting some to ObjC++ and adding dispatch wrappers 2013-01-12 15:38:00 -08:00
61 changed files with 9387 additions and 315 deletions

3
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "pages"]
path = pages
url = git://github.com/square/SocketRocket.git
[submodule "Frameworks/RxSwift"]
path = Frameworks/RxSwift
url = https://github.com/ReactiveX/RxSwift.git

1
Frameworks/RxSwift Submodule

@ -0,0 +1 @@
Subproject commit 1c47f0a79231f889a6c954023d857a3b5344ac88

View File

@ -107,6 +107,7 @@
- (NSUInteger)testCaseCount;
{
return 0;
if (self.invocation) {
return [super testCaseCount];
}

View File

@ -7,7 +7,7 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.squareup.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>

View File

@ -0,0 +1,13 @@
//
// STRDispatchTest.h
// SocketRocket
//
// Created by Michael Lewis on 1/12/13.
//
//
#import <SenTestingKit/SenTestingKit.h>
@interface STRDispatchTest : SenTestCase
@end

View File

@ -0,0 +1,151 @@
//
// STRDispatchTest.m
// SocketRocket
//
// Created by Michael Lewis on 1/12/13.
//
//
extern "C" {
#import <SenTestingKit/SenTestingKit.h>
}
#include <Security/SecureTransport.h>
#include "DispatchIO.h"
#include "DispatchData.h"
#include "SecureIO.h"
using namespace squareup::dispatch;
@interface STRDispatchTest : SenTestCase
@end
@implementation STRDispatchTest
- (void)testConnect;
{
bool finished = false;
dispatch_queue_t workQueue = dispatch_queue_create("dispatch queue", DISPATCH_QUEUE_SERIAL);
Dial(workQueue, "localhost", "9932", dispatch_get_main_queue(), [&](dispatch_fd_t fd, int error_code, const char *error_message) {
NSLog(@"code: %d, msg: %s", error_code, error_message);
STAssertEquals(error_code, 0, @"Should not error but got %s", error_message);
finished = true;
});
[self runCurrentRunLoopUntilTestPasses:[&finished](){
return (BOOL)finished;
} timeout:100.0];
}
- (void)testSimpleDial;
{
RawIO *raw_io = nullptr;
bool finished = false;
auto cleanupBlock = [&finished](int error) {
finished = true;
};
SimpleDial("localhost", "9934", dispatch_get_main_queue(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [&raw_io, self, &finished](squareup::dispatch::RawIO *io, int error, const char *error_message) {
STAssertEquals(error, 0, @"Should not have errored, but got %s", error_message);
STAssertTrue(io != nullptr, @"io should be valid");
if (!io) {
finished = true;
return;
}
raw_io = io;
io->Write(Data("HELLO THERE!", dispatch_get_main_queue()), dispatch_get_main_queue(), [self, &raw_io](bool done, dispatch_data_t data, int error) {
STAssertEquals(error, 0, @"Error should == 0");
if (done) {
raw_io->Close(0);
}
});
}, cleanupBlock);
[self runCurrentRunLoopUntilTestPasses:[&finished](){
return (BOOL)finished;
} timeout:100.0];
}
- (void)testDialTLS;
{
SecureIO *raw_io = nullptr;
bool finished = false;
auto cleanupBlock = [&finished, raw_io](int error) {
dispatch_async(dispatch_get_main_queue(), [raw_io]{
delete raw_io;
});
NSLog(@"FINISHED");
finished = true;
};
SSLContextRef ctx = SSLCreateContext(CFAllocatorGetDefault(), kSSLClientSide, kSSLStreamType);
DialTLS("10.0.1.15", "10248", ctx, dispatch_get_main_queue(), dispatch_get_main_queue(), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [&raw_io, self, &finished](squareup::dispatch::SecureIO *io, int error, const char *error_message) {
STAssertEquals(error, 0, @"Should not have errored, but got %s", error_message);
STAssertTrue(io != nullptr, @"io should be valid");
if (!io) {
finished = true;
return;
}
raw_io = io;
__block bool seenInner = false;
__block bool seenOuter = false;
//
// raw_io->Write(Data("HELLO THERE!\n", dispatch_get_main_queue()), ^(bool done, dispatch_data_t data, int error) {
// STAssertEquals(error, 0, @"Error should == 0");
// STAssertFalse(seenOuter, @"Should only see the outer once");
// if (done) {
// seenOuter = true;
// }
// if (done && !error) {
//
// }
// });
//
// raw_io->Write(Data("HELLO THERE2!\n", dispatch_get_main_queue()), ^(bool done, dispatch_data_t data, int error) {
// STAssertFalse(seenInner, @"Shouldn't have seen inner yet");
// if (done) {
// seenInner = done;
// }
// STAssertEquals(error, 0, @"Error should == 0");
// STAssertFalse(finished, @"Shouldn't have finished");
// });
raw_io->Read(SIZE_MAX, dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
if (!error) {
raw_io->Write(data, dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
});
} else {
STAssertTrue(error == ECANCELED, @"Server should terminate");
if (done && !error) {
raw_io->Close(0);
}
}
});
}, cleanupBlock);
[self runCurrentRunLoopUntilTestPasses:[&finished](){
return (BOOL)finished;
} timeout:100.0];
}
@end

View File

@ -1,4 +1,4 @@
//
//
// Copyright 2012 Square Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,878 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
<<<<<<< HEAD
2725F85B16A29D18007018E9 /* SecureIO.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2725F85916A29D18007018E9 /* SecureIO.mm */; };
2725F85C16A29D18007018E9 /* SecureIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 2725F85A16A29D18007018E9 /* SecureIO.h */; };
27CA136116A21E4D00A35C2D /* DispatchIO.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27CA135F16A21E4D00A35C2D /* DispatchIO.mm */; };
27CA136216A21E4D00A35C2D /* DispatchIO.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CA136016A21E4D00A35C2D /* DispatchIO.h */; };
27CA136616A2243000A35C2D /* DispatchData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27CA136416A2243000A35C2D /* DispatchData.mm */; };
27CA136716A2243000A35C2D /* DispatchData.h in Headers */ = {isa = PBXBuildFile; fileRef = 27CA136516A2243000A35C2D /* DispatchData.h */; };
27CA136A16A23AAE00A35C2D /* STRDispatchTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 27CA136916A23AAE00A35C2D /* STRDispatchTest.mm */; };
27DC4A9516A64EB800E9C084 /* Common.h in Headers */ = {isa = PBXBuildFile; fileRef = 27DC4A9416A64EB800E9C084 /* Common.h */; };
F6016C7C146124B20037BB3D /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = F6016C7B146124B20037BB3D /* base64.c */; };
F6016C7F146124ED0037BB3D /* base64.h in Headers */ = {isa = PBXBuildFile; fileRef = F6016C7E146124ED0037BB3D /* base64.h */; };
=======
>>>>>>> origin/master
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 */; };
F60CC2A114D4EA0500A005E4 /* SRTWebSocketOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */; };
F61A0DC81625F44D00365EBD /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F61A0DC71625F44D00365EBD /* Default-568h@2x.png */; };
F62417E614D52F3C003CE997 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F62417E514D52F3C003CE997 /* UIKit.framework */; };
F62417E714D52F3C003CE997 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B208301450F597009315AF /* Foundation.framework */; };
F62417E914D52F3C003CE997 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F62417E814D52F3C003CE997 /* CoreGraphics.framework */; };
F62417EF14D52F3C003CE997 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F62417ED14D52F3C003CE997 /* InfoPlist.strings */; };
F62417F114D52F3C003CE997 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F62417F014D52F3C003CE997 /* main.m */; };
F62417F514D52F3C003CE997 /* TCAppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = F62417F414D52F3C003CE997 /* TCAppDelegate.mm */; };
F62417F814D52F3C003CE997 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */; };
F62417FB14D52F3C003CE997 /* TCViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F62417FA14D52F3C003CE997 /* TCViewController.m */; };
F624180114D5300C003CE997 /* TCChatCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F624180014D5300C003CE997 /* TCChatCell.m */; };
F624180214D532E0003CE997 /* libSocketRocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F6B2082D1450F597009315AF /* libSocketRocket.a */; };
F624180314D53449003CE997 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD51451231B00C1D980 /* CFNetwork.framework */; };
F624180414D53449003CE997 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F6A12CD3145122FC00C1D980 /* Security.framework */; };
F624180614D53451003CE997 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
<<<<<<< HEAD
F6396B86153E67EC00345B5E /* SRWebSocket.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.mm */; };
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 */; };
=======
F6396B86153E67EC00345B5E /* SRWebSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.m */; };
>>>>>>> origin/master
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, ); }; };
<<<<<<< HEAD
F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = F6A12CCF145119B700C1D980 /* SRWebSocket.h */; };
F6A12CD2145119B700C1D980 /* SRWebSocket.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6A12CD0145119B700C1D980 /* SRWebSocket.mm */; };
=======
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 */; };
>>>>>>> origin/master
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 */; };
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 */; };
F6C41C98145F7C6100641356 /* libicucore.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F6C41C95145F7C4700641356 /* libicucore.dylib */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
F62417D514D50869003CE997 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = F6B208241450F597009315AF /* Project object */;
proxyType = 1;
remoteGlobalIDString = F6B2082C1450F597009315AF;
remoteInfo = SocketRocket;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
<<<<<<< HEAD
2725F85916A29D18007018E9 /* SecureIO.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SecureIO.mm; sourceTree = "<group>"; };
2725F85A16A29D18007018E9 /* SecureIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SecureIO.h; sourceTree = "<group>"; };
27CA135F16A21E4D00A35C2D /* DispatchIO.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatchIO.mm; sourceTree = "<group>"; };
27CA136016A21E4D00A35C2D /* DispatchIO.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DispatchIO.h; sourceTree = "<group>"; };
27CA136416A2243000A35C2D /* DispatchData.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = DispatchData.mm; sourceTree = "<group>"; };
27CA136516A2243000A35C2D /* DispatchData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DispatchData.h; sourceTree = "<group>"; };
27CA136916A23AAE00A35C2D /* STRDispatchTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = STRDispatchTest.mm; sourceTree = "<group>"; };
27DC4A9416A64EB800E9C084 /* Common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Common.h; sourceTree = "<group>"; };
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>"; };
=======
>>>>>>> origin/master
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>"; };
F62417E314D52F3C003CE997 /* TestChat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestChat.app; sourceTree = BUILT_PRODUCTS_DIR; };
F62417E514D52F3C003CE997 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
F62417E814D52F3C003CE997 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
F62417EC14D52F3C003CE997 /* TestChat-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestChat-Info.plist"; sourceTree = "<group>"; };
F62417EE14D52F3C003CE997 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
F62417F014D52F3C003CE997 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
F62417F214D52F3C003CE997 /* TestChat-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TestChat-Prefix.pch"; sourceTree = "<group>"; };
F62417F314D52F3C003CE997 /* TCAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TCAppDelegate.h; sourceTree = "<group>"; };
F62417F414D52F3C003CE997 /* TCAppDelegate.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TCAppDelegate.mm; sourceTree = "<group>"; };
F62417F714D52F3C003CE997 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/MainStoryboard.storyboard; sourceTree = "<group>"; };
F62417F914D52F3C003CE997 /* TCViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TCViewController.h; sourceTree = "<group>"; };
F62417FA14D52F3C003CE997 /* TCViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TCViewController.m; sourceTree = "<group>"; };
F62417FF14D5300C003CE997 /* TCChatCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TCChatCell.h; sourceTree = "<group>"; };
F624180014D5300C003CE997 /* TCChatCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TCChatCell.m; sourceTree = "<group>"; };
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; };
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; };
F668C886153E91210044DBAC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
F668C889153E91210044DBAC /* SocketRocketOSX-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SocketRocketOSX-Info.plist"; sourceTree = "<group>"; };
F6A12CCF145119B700C1D980 /* SRWebSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SRWebSocket.h; sourceTree = "<group>"; };
F6A12CD0145119B700C1D980 /* SRWebSocket.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SRWebSocket.mm; 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>"; };
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>"; };
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 */
F62417E014D52F3C003CE997 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F62417E614D52F3C003CE997 /* UIKit.framework in Frameworks */,
F62417E714D52F3C003CE997 /* Foundation.framework in Frameworks */,
F62417E914D52F3C003CE997 /* CoreGraphics.framework in Frameworks */,
F624180214D532E0003CE997 /* libSocketRocket.a in Frameworks */,
F624180314D53449003CE997 /* CFNetwork.framework in Frameworks */,
F624180414D53449003CE997 /* Security.framework in Frameworks */,
F624180614D53451003CE997 /* libicucore.dylib in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F668C87C153E91210044DBAC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F668C899153E923C0044DBAC /* CoreServices.framework in Frameworks */,
F668C89A153E923C0044DBAC /* Foundation.framework in Frameworks */,
F668C89B153E923C0044DBAC /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F6B2082A1450F597009315AF /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F6AE4520145906B20022AF3C /* Foundation.framework in Frameworks */,
F6016C8914620ECC0037BB3D /* Security.framework in Frameworks */,
F6016C8A1462143C0037BB3D /* CFNetwork.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F6BDA7FE145900D200FE3253 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
F6C41C98145F7C6100641356 /* libicucore.dylib in Frameworks */,
F6BDA806145900D200FE3253 /* Foundation.framework in Frameworks */,
F6AE451D145906A70022AF3C /* libSocketRocket.a in Frameworks */,
F6AE45241459071C0022AF3C /* CFNetwork.framework in Frameworks */,
F6016C8814620EC70037BB3D /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
F62417EA14D52F3C003CE997 /* TestChat */ = {
isa = PBXGroup;
children = (
F62417EB14D52F3C003CE997 /* Supporting Files */,
F62417F314D52F3C003CE997 /* TCAppDelegate.h */,
F62417F414D52F3C003CE997 /* TCAppDelegate.mm */,
F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */,
F62417F914D52F3C003CE997 /* TCViewController.h */,
F62417FA14D52F3C003CE997 /* TCViewController.m */,
F62417FF14D5300C003CE997 /* TCChatCell.h */,
F624180014D5300C003CE997 /* TCChatCell.m */,
);
path = TestChat;
sourceTree = "<group>";
};
F62417EB14D52F3C003CE997 /* Supporting Files */ = {
isa = PBXGroup;
children = (
F61A0DC71625F44D00365EBD /* Default-568h@2x.png */,
F62417EC14D52F3C003CE997 /* TestChat-Info.plist */,
F62417ED14D52F3C003CE997 /* InfoPlist.strings */,
F62417F014D52F3C003CE997 /* main.m */,
F62417F214D52F3C003CE997 /* TestChat-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
F6396BA3153E6D4D00345B5E /* OSX Frameworks */ = {
isa = PBXGroup;
children = (
F6396BA4153E6D7400345B5E /* CoreServices.framework */,
F6396BA1153E6D4800345B5E /* Foundation.framework */,
F6396B9F153E6D3700345B5E /* Security.framework */,
);
name = "OSX Frameworks";
sourceTree = "<group>";
};
F668C883153E91210044DBAC /* Other Frameworks */ = {
isa = PBXGroup;
children = (
F668C884153E91210044DBAC /* AppKit.framework */,
F668C885153E91210044DBAC /* CoreData.framework */,
F668C886153E91210044DBAC /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
F668C887153E91210044DBAC /* SocketRocketOSX */ = {
isa = PBXGroup;
children = (
F668C889153E91210044DBAC /* SocketRocketOSX-Info.plist */,
);
path = SocketRocketOSX;
sourceTree = "<group>";
};
F6B208221450F597009315AF = {
isa = PBXGroup;
children = (
F6B208321450F597009315AF /* SocketRocket */,
F6BDA807145900D200FE3253 /* SRWebSocketTests */,
F62417EA14D52F3C003CE997 /* TestChat */,
F668C887153E91210044DBAC /* SocketRocketOSX */,
F6B2082F1450F597009315AF /* Frameworks */,
F6B2082E1450F597009315AF /* Products */,
);
indentWidth = 4;
sourceTree = "<group>";
tabWidth = 4;
};
F6B2082E1450F597009315AF /* Products */ = {
isa = PBXGroup;
children = (
F6B2082D1450F597009315AF /* libSocketRocket.a */,
F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */,
F62417E314D52F3C003CE997 /* TestChat.app */,
F668C880153E91210044DBAC /* SocketRocket.framework */,
);
name = Products;
sourceTree = "<group>";
};
F6B2082F1450F597009315AF /* Frameworks */ = {
isa = PBXGroup;
children = (
F6396BA3153E6D4D00345B5E /* OSX Frameworks */,
F6C41C95145F7C4700641356 /* libicucore.dylib */,
F6A12CD51451231B00C1D980 /* CFNetwork.framework */,
F6A12CD3145122FC00C1D980 /* Security.framework */,
F6B208301450F597009315AF /* Foundation.framework */,
F62417E514D52F3C003CE997 /* UIKit.framework */,
F62417E814D52F3C003CE997 /* CoreGraphics.framework */,
F668C883153E91210044DBAC /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
F6B208321450F597009315AF /* SocketRocket */ = {
isa = PBXGroup;
children = (
F6B208331450F597009315AF /* Supporting Files */,
F6A12CCF145119B700C1D980 /* SRWebSocket.h */,
<<<<<<< HEAD
F6A12CD0145119B700C1D980 /* SRWebSocket.mm */,
F6572123146C7B6A00D6B8A9 /* NSData+SRB64Additions.h */,
F6572124146C7B6A00D6B8A9 /* NSData+SRB64Additions.m */,
27CA135F16A21E4D00A35C2D /* DispatchIO.mm */,
27CA136016A21E4D00A35C2D /* DispatchIO.h */,
2725F85916A29D18007018E9 /* SecureIO.mm */,
2725F85A16A29D18007018E9 /* SecureIO.h */,
27CA136416A2243000A35C2D /* DispatchData.mm */,
27CA136516A2243000A35C2D /* DispatchData.h */,
27DC4A9416A64EB800E9C084 /* Common.h */,
=======
F6A12CD0145119B700C1D980 /* SRWebSocket.m */,
>>>>>>> origin/master
);
path = SocketRocket;
sourceTree = "<group>";
};
F6B208331450F597009315AF /* Supporting Files */ = {
isa = PBXGroup;
children = (
F6B208341450F597009315AF /* SocketRocket-Prefix.pch */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
F6BDA807145900D200FE3253 /* SRWebSocketTests */ = {
isa = PBXGroup;
children = (
F6BDA808145900D200FE3253 /* Supporting Files */,
F6BDA8151459016900FE3253 /* SRTAutobahnTests.m */,
F6BDA810145900D200FE3253 /* SRWebSocketTests-Prefix.pch */,
F6AE4526145907D30022AF3C /* SenTestCase+SRTAdditions.h */,
F6AE4527145907D30022AF3C /* SenTestCase+SRTAdditions.m */,
F60CC29F14D4EA0500A005E4 /* SRTWebSocketOperation.h */,
F60CC2A014D4EA0500A005E4 /* SRTWebSocketOperation.m */,
27CA136916A23AAE00A35C2D /* STRDispatchTest.mm */,
);
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 */
F668C87D153E91210044DBAC /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
F668C8AA153E92F90044DBAC /* SRWebSocket.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F6B2082B1450F597009315AF /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
F6A12CD1145119B700C1D980 /* SRWebSocket.h in Headers */,
<<<<<<< HEAD
F6016C7F146124ED0037BB3D /* base64.h in Headers */,
27CA136216A21E4D00A35C2D /* DispatchIO.h in Headers */,
27CA136716A2243000A35C2D /* DispatchData.h in Headers */,
2725F85C16A29D18007018E9 /* SecureIO.h in Headers */,
27DC4A9516A64EB800E9C084 /* Common.h in Headers */,
=======
>>>>>>> origin/master
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
F62417E214D52F3C003CE997 /* TestChat */ = {
isa = PBXNativeTarget;
buildConfigurationList = F62417FC14D52F3C003CE997 /* Build configuration list for PBXNativeTarget "TestChat" */;
buildPhases = (
F62417DF14D52F3C003CE997 /* Sources */,
F62417E014D52F3C003CE997 /* Frameworks */,
F62417E114D52F3C003CE997 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = TestChat;
productName = TestChat;
productReference = F62417E314D52F3C003CE997 /* TestChat.app */;
productType = "com.apple.product-type.application";
};
F668C87F153E91210044DBAC /* SocketRocketOSX */ = {
isa = PBXNativeTarget;
buildConfigurationList = F668C891153E91210044DBAC /* Build configuration list for PBXNativeTarget "SocketRocketOSX" */;
buildPhases = (
F6396B85153E67EC00345B5E /* Sources */,
F668C87C153E91210044DBAC /* Frameworks */,
F668C87D153E91210044DBAC /* Headers */,
F668C87E153E91210044DBAC /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SocketRocketOSX;
productName = SocketRocketOSX;
productReference = F668C880153E91210044DBAC /* SocketRocket.framework */;
productType = "com.apple.product-type.framework";
};
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";
};
F6BDA801145900D200FE3253 /* SRWebSocketTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = F6BDA813145900D200FE3253 /* Build configuration list for PBXNativeTarget "SRWebSocketTests" */;
buildPhases = (
F6BDA7FD145900D200FE3253 /* Sources */,
F6BDA7FE145900D200FE3253 /* Frameworks */,
F6BDA7FF145900D200FE3253 /* Resources */,
);
buildRules = (
);
dependencies = (
F62417D614D50869003CE997 /* PBXTargetDependency */,
);
name = SRWebSocketTests;
productName = SRWebSocketTests;
productReference = F6BDA802145900D200FE3253 /* SRWebSocketTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
F6B208241450F597009315AF /* Project object */ = {
isa = PBXProject;
attributes = {
LastTestingUpgradeCheck = 0640;
LastUpgradeCheck = 0640;
};
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 */,
F668C87F153E91210044DBAC /* SocketRocketOSX */,
F6BDA801145900D200FE3253 /* SRWebSocketTests */,
F62417E214D52F3C003CE997 /* TestChat */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
F62417E114D52F3C003CE997 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F62417EF14D52F3C003CE997 /* InfoPlist.strings in Resources */,
F62417F814D52F3C003CE997 /* MainStoryboard.storyboard in Resources */,
F61A0DC81625F44D00365EBD /* Default-568h@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F668C87E153E91210044DBAC /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
F6BDA7FF145900D200FE3253 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F6BDA80C145900D200FE3253 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
F62417DF14D52F3C003CE997 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F62417F114D52F3C003CE997 /* main.m in Sources */,
F62417F514D52F3C003CE997 /* TCAppDelegate.mm in Sources */,
F62417FB14D52F3C003CE997 /* TCViewController.m in Sources */,
F624180114D5300C003CE997 /* TCChatCell.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F6396B85153E67EC00345B5E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
<<<<<<< HEAD
F6396B86153E67EC00345B5E /* SRWebSocket.mm in Sources */,
F6396B87153E67EC00345B5E /* base64.c in Sources */,
F6396B88153E67EC00345B5E /* NSData+SRB64Additions.m in Sources */,
=======
F6396B86153E67EC00345B5E /* SRWebSocket.m in Sources */,
>>>>>>> origin/master
);
runOnlyForDeploymentPostprocessing = 0;
};
F6B208291450F597009315AF /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
<<<<<<< HEAD
F6A12CD2145119B700C1D980 /* SRWebSocket.mm in Sources */,
F6016C7C146124B20037BB3D /* base64.c in Sources */,
F6572126146C7B6A00D6B8A9 /* NSData+SRB64Additions.m in Sources */,
27CA136116A21E4D00A35C2D /* DispatchIO.mm in Sources */,
27CA136616A2243000A35C2D /* DispatchData.mm in Sources */,
2725F85B16A29D18007018E9 /* SecureIO.mm in Sources */,
=======
F6A12CD2145119B700C1D980 /* SRWebSocket.m in Sources */,
>>>>>>> origin/master
);
runOnlyForDeploymentPostprocessing = 0;
};
F6BDA7FD145900D200FE3253 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F6BDA8161459016900FE3253 /* SRTAutobahnTests.m in Sources */,
F6AE4528145907D30022AF3C /* SenTestCase+SRTAdditions.m in Sources */,
F60CC2A114D4EA0500A005E4 /* SRTWebSocketOperation.m in Sources */,
27CA136A16A23AAE00A35C2D /* STRDispatchTest.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
F62417D614D50869003CE997 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = F6B2082C1450F597009315AF /* SocketRocket */;
targetProxy = F62417D514D50869003CE997 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
F62417ED14D52F3C003CE997 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
F62417EE14D52F3C003CE997 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
F62417F614D52F3C003CE997 /* MainStoryboard.storyboard */ = {
isa = PBXVariantGroup;
children = (
F62417F714D52F3C003CE997 /* en */,
);
name = MainStoryboard.storyboard;
sourceTree = "<group>";
};
F6BDA80A145900D200FE3253 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
F6BDA80B145900D200FE3253 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
F62417FD14D52F3C003CE997 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "TestChat/TestChat-Prefix.pch";
INFOPLIST_FILE = "TestChat/TestChat-Info.plist";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
F62417FE14D52F3C003CE997 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(DEVELOPER_FRAMEWORKS_DIR)\"",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "TestChat/TestChat-Prefix.pch";
INFOPLIST_FILE = "TestChat/TestChat-Info.plist";
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
TARGETED_DEVICE_FAMILY = "1,2";
WRAPPER_EXTENSION = app;
};
name = Release;
};
F668C892153E91210044DBAC /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks\"",
);
FRAMEWORK_VERSION = A;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "SocketRocket/SocketRocket-Prefix.pch";
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
INFOPLIST_FILE = "SocketRocketOSX/SocketRocketOSX-Info.plist";
LD_DYLIB_INSTALL_NAME = "@executable_path/../Frameworks/$(EXECUTABLE_PATH)";
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = SocketRocket;
SDKROOT = macosx;
WRAPPER_EXTENSION = framework;
};
name = Debug;
};
F668C893153E91210044DBAC /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SYSTEM_APPS_DIR)/Xcode.app/Contents/Developer/Library/Frameworks\"",
);
FRAMEWORK_VERSION = A;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "SocketRocket/SocketRocket-Prefix.pch";
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
INFOPLIST_FILE = "SocketRocketOSX/SocketRocketOSX-Info.plist";
LD_DYLIB_INSTALL_NAME = "@executable_path/../Frameworks/$(EXECUTABLE_PATH)";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = SocketRocket;
SDKROOT = macosx;
WRAPPER_EXTENSION = framework;
};
name = Release;
};
F6B208381450F597009315AF /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
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;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.1;
ONLY_ACTIVE_ARCH = YES;
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos6.0;
};
name = Debug;
};
F6B208391450F597009315AF /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "c++0x";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
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;
GCC_WARN_SIGN_COMPARE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 5.1;
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos6.0;
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/iPhoneSimulator7.1.sdk/usr/lib/system\"",
"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib\"",
);
OTHER_LDFLAGS = "-Licucore";
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/iPhoneSimulator7.1.sdk/usr/lib/system\"",
"\"$(DEVELOPER_DIR)/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.1.sdk/usr/lib\"",
);
OTHER_LDFLAGS = "-Licucore";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
F6BDA811145900D200FE3253 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "SRWebSocketTests/SRWebSocketTests-Prefix.pch";
INFOPLIST_FILE = "SRWebSocketTests/SRWebSocketTests-Info.plist";
OTHER_LDFLAGS = (
"-all_load",
"-ObjC",
"-framework",
XCTest,
);
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
F6BDA812145900D200FE3253 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
FRAMEWORK_SEARCH_PATHS = (
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "SRWebSocketTests/SRWebSocketTests-Prefix.pch";
INFOPLIST_FILE = "SRWebSocketTests/SRWebSocketTests-Info.plist";
OTHER_LDFLAGS = (
"-all_load",
"-ObjC",
"-framework",
XCTest,
);
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
F62417FC14D52F3C003CE997 /* Build configuration list for PBXNativeTarget "TestChat" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F62417FD14D52F3C003CE997 /* Debug */,
F62417FE14D52F3C003CE997 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
F668C891153E91210044DBAC /* Build configuration list for PBXNativeTarget "SocketRocketOSX" */ = {
isa = XCConfigurationList;
buildConfigurations = (
F668C892153E91210044DBAC /* Debug */,
F668C893153E91210044DBAC /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
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;
};
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 */;
}

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
version = "1.7">
LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
@ -14,79 +14,79 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6B2082C1450F597009315AF"
BuildableName = "libSocketRocket.a"
BlueprintIdentifier = "F6F199B51C3E48A70068E02B"
BuildableName = "SocketRocket.framework"
BlueprintName = "SocketRocket"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6BDA801145900D200FE3253"
BuildableName = "SRWebSocketTests.xctest"
BlueprintName = "SRWebSocketTests"
BlueprintIdentifier = "F69DAA1C1B6AEF6900B456D0"
BuildableName = "SocketRocketIOTests.xctest"
BlueprintName = "SocketRocketIOTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F69DAA131B6AEF6900B456D0"
BuildableName = "SocketRocketIO.framework"
BlueprintName = "SocketRocketIO"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6F199BE1C3E48A70068E02B"
BuildableName = "SocketRocketTests.xctest"
BlueprintName = "SocketRocketTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO"
buildConfiguration = "Debug">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "PIDFILE=$TMPDIR/srtharness.pid&#10;if [ -r $PIDFILE ]; then&#10; EXISTING_PID=`cat $PIDFILE`&#10; echo &quot;Killing Dangling SRTextharneess PID:&quot; $EXISTING_PID&#10; kill $EXISTING_PID&#10; rm $PIDFILE&#10;fi&#10;&#10;pushd $PROJECT_DIR&#10;&#10;export MACOSX_DEPLOYMENT_TARGET=&#10;&#10;bash TestSupport/ensure_virtualenv.sh $PROJECT_DIR/.env&#10;source .env/bin/activate&#10;&#10;rm -rf &quot;$PROJECT_DIR/reports/clients/&quot;&#10;nohup sr-testharness -i &apos;&apos; -c &apos;*&apos; &amp;&#10;&#10;#nohup sr-testharness -k hello_test_harness -i &apos;&apos; -c &apos;*&apos; &amp;&#10;&#10;echo $! &gt; $PIDFILE&#10;&#10;popd"
shellToInvoke = "/bin/bash">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6BDA801145900D200FE3253"
BuildableName = "SRWebSocketTests.xctest"
BlueprintName = "SRWebSocketTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<PostActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Script"
scriptText = "PIDFILE=$TMPDIR/srtharness.pid&#10;if [ -r $PIDFILE ]; then&#10; EXISTING_PID=`cat $PIDFILE`&#10; echo &quot;Killing SRTextharneess PID:&quot; $EXISTING_PID&#10; kill $EXISTING_PID&#10; rm $PIDFILE&#10;fi&#10;&#10;&#10;open $PROJECT_DIR/reports/clients/index.html"
shellToInvoke = "/bin/bash">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6BDA801145900D200FE3253"
BuildableName = "SRWebSocketTests.xctest"
BlueprintName = "SRWebSocketTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PostActions>
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6BDA801145900D200FE3253"
BuildableName = "SRWebSocketTests.xctest"
BlueprintName = "SRWebSocketTests"
BlueprintIdentifier = "F69DAA1C1B6AEF6900B456D0"
BuildableName = "SocketRocketIOTests.xctest"
BlueprintName = "SocketRocketIOTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6F199BE1C3E48A70068E02B"
BuildableName = "SocketRocketTests.xctest"
BlueprintName = "SocketRocketTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</TestableReference>
@ -94,67 +94,52 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6B2082C1450F597009315AF"
BuildableName = "libSocketRocket.a"
BlueprintName = "SocketRocket"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "SR_TEST_URL"
value = "ws://localhost:9001"
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6B2082C1450F597009315AF"
BuildableName = "libSocketRocket.a"
BlueprintIdentifier = "F6F199B51C3E48A70068E02B"
BuildableName = "SocketRocket.framework"
BlueprintName = "SocketRocket"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6F199B51C3E48A70068E02B"
BuildableName = "SocketRocket.framework"
BlueprintName = "SocketRocket"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "OBJC_PRINT_EXCEPTIONS"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocGuardEdges"
value = ""
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6F199B51C3E48A70068E02B"
BuildableName = "SocketRocket.framework"
BlueprintName = "SocketRocket"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -23,21 +23,24 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
@ -52,10 +55,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -23,10 +23,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "NO"
buildConfiguration = "Debug">
shouldUseLaunchSchemeArgsEnv = "NO">
<Testables>
<TestableReference
skipped = "NO">
@ -39,15 +39,18 @@
</BuildableReference>
</TestableReference>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
@ -82,10 +85,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -23,11 +23,51 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.GDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6191F401B6C4694003DF396"
BuildableName = "SystemShimsTests.xctest"
BlueprintName = "SystemShimsTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6F199741C3E476C0068E02B"
BuildableName = "SocketRocketBaseTests.xctest"
BlueprintName = "SocketRocketBaseTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6F1999C1C3E486D0068E02B"
BuildableName = "SocketRocketLiteTests.xctest"
BlueprintName = "SocketRocketLiteTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</TestableReference>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F6F199BE1C3E48A70068E02B"
BuildableName = "SocketRocketTests.xctest"
BlueprintName = "SocketRocketTests"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
@ -38,15 +78,18 @@
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@ -59,33 +102,13 @@
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "OBJC_PRINT_EXCEPTIONS"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocGuardEdges"
value = ""
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0630"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F62417E214D52F3C003CE997"
BuildableName = "TestChat.app"
BlueprintName = "TestChat"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Debug">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F62417E214D52F3C003CE997"
BuildableName = "TestChat.app"
BlueprintName = "TestChat"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
<LaunchAction
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F62417E214D52F3C003CE997"
BuildableName = "TestChat.app"
BlueprintName = "TestChat"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
<AdditionalOption
<<<<<<< HEAD
=======
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
>>>>>>> origin/master
key = "OBJC_PRINT_EXCEPTIONS"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
<<<<<<< HEAD
=======
<AdditionalOption
key = "MallocGuardEdges"
value = ""
isEnabled = "YES">
</AdditionalOption>
<AdditionalOption
key = "MallocScribble"
value = ""
isEnabled = "YES">
</AdditionalOption>
>>>>>>> origin/master
</AdditionalOptions>
</LaunchAction>
<ProfileAction
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F62417E214D52F3C003CE997"
BuildableName = "TestChat.app"
BlueprintName = "TestChat"
ReferencedContainer = "container:SocketRocket.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

32
SocketRocket/Common.h Normal file
View File

@ -0,0 +1,32 @@
//
// Common.h
// SocketRocket
//
// Created by Michael Lewis on 1/15/13.
//
//
#ifndef SocketRocket_Common_h
#define SocketRocket_Common_h
#ifdef __cplusplus
extern "C" {
#endif
#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
#define sr_dispatch_retain(x)
#define sr_dispatch_release(x)
#define __sr_maybe_bridge__ __bridge
#define __sr_maybe_strong__ __strong
#else
#define sr_dispatch_retain(x) dispatch_retain(x)
#define sr_dispatch_release(x) dispatch_release(x)
#define __sr_maybe_bridge__
#define __sr_maybe_strong__
#endif
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,77 @@
//
// WebSocketVersionNumber.swift
// SocketRocket
//
// Created by Mike Lewis on 1/7/16.
//
//
import Foundation
/// Versions defined in RFC6455 section 11.6 https://tools.ietf.org/html/rfc6455#section-11.6
/// We only care about one currently
public enum WebSocketVersionNumber : Int {
/// Standard version number
case RFC6455 = 13
}
/// opcodes defined in RFC6455 section 11.8 https://tools.ietf.org/html/rfc6455#section-11.8
public enum WebSocketOpcode : Int {
case Continuation = 0
case Text = 1
case Binary = 2
case ConnectionClose = 3
case Ping = 9
case Pong = 10
}
/// Known close codes. These are defined in https://tools.ietf.org/html/rfc6455#section-7.4
public enum WebSocketCloseCode : Int {
case NormalClosure = 1000
case GoingAway = 1001
case ProtocolError = 1002
case UnsupportedData = 1003
// 1004 is reserved
case NoStatusRcvd = 1005
case AbnormalClosure = 1006
case InvalidFramePayloadData = 1007
case PolicyViolation = 1008
case MessageTooBig = 1009
case MandatoryExt = 1010
case InternalServerError = 1011
case TLSHandshake = 1015
}
/// Represents either a known close code or unknown
public enum AnyWebSocketCloseCode {
/// This is for known close codes
case Known(WebSocketCloseCode)
/// Unknown close codes
case Unknown(Int)
init(_ code: Int){
if let knownCode = WebSocketCloseCode(rawValue: code) {
self = .Known(knownCode)
} else {
self = .Unknown(code)
}
}
public var code: Int {
switch self {
case let .Known(val):
return val.rawValue
case let .Unknown(val):
return val
}
}
/// Whether or not it was a clean closure
public var isClean: Bool {
switch self {
case .Known(.NormalClosure):
return true
default:
return false
}
}
}

149
SocketRocket/DispatchData.h Normal file
View File

@ -0,0 +1,149 @@
//
// DispatchData.h
// SocketRocket
//
// Created by Michael Lewis on 1/12/13.
//
//
#ifndef __SocketRocket__DispatchData__
#define __SocketRocket__DispatchData__
#include <string.h>
#include <iostream>
#include <dispatch/dispatch.h>
#include <deque>
#include "Common.h"
namespace squareup {
namespace dispatch {
class Data {
__sr_maybe_strong__ dispatch_data_t _data;
public:
inline Data() : Data(dispatch_data_empty) {
}
// Initializes a new data. retains the data by default
// Data always releases _data;
inline Data(dispatch_data_t data, bool retain = true) : _data(data) {
if (retain && _data) {
sr_dispatch_retain(_data);
}
}
// Will copy the data
inline Data(const char *str, dispatch_queue_t release_queue) :
_data(dispatch_data_create(reinterpret_cast<const void *>(str), strlen(str), release_queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT)) {
}
// Copy constructor
inline Data(const Data &other) : Data(static_cast<dispatch_data_t>(other), true) {
}
inline Data(const void *bytes, size_t length, dispatch_queue_t release_queue) :
_data(dispatch_data_create(bytes, length, release_queue, DISPATCH_DATA_DESTRUCTOR_DEFAULT)) {
}
inline Data Concat(dispatch_data_t other) const {
return Data(dispatch_data_create_concat(_data, other), false);
}
inline Data operator + (dispatch_data_t other) const {
return Concat(other);
}
inline Data Subrange(size_t offset, size_t length) const {
return Data(dispatch_data_create_subrange(_data, offset, length), false);
}
// copies bytes into the buffer, and returns the remaining.
inline Data TakeInto(size_t length, void *bytes) const {
size_t size = Size();
assert(length <= size);
Apply([&](dispatch_data_t region, size_t offset, const void *buffer, size_t size) -> bool {
size_t numToCopy = std::min(size, length - offset);
memcpy(reinterpret_cast<void *>(reinterpret_cast<uint8_t *>(bytes) + offset), buffer, numToCopy);
return size + offset <= length;
});
if (length == size) {
return dispatch_data_empty;
}
return Subrange(length, size - length);
}
inline Data Map(const void **buffer_ptr, size_t *size_ptr) const {
return Data(dispatch_data_create_map(_data, buffer_ptr, size_ptr), false);
}
inline Data CopyRegion(size_t location, size_t *offset_ptr) const {
return Data(dispatch_data_copy_region(_data, location, offset_ptr), false);
}
inline bool Apply(dispatch_data_applier_t applier) const {
return dispatch_data_apply(_data, applier);
}
inline size_t Size() const {
return dispatch_data_get_size(static_cast<dispatch_data_t>(*this));
}
// This is a workaround since the memory gets quite fragmented if you use it as a stream
inline void FlattenIfNecessary() {
if (NumRegions() > 100) {
size_t size;
const void * buffer;
(*this) = Data(Map(&buffer, &size));
}
}
inline size_t NumRegions() {
size_t ret = 0;
Apply([&ret](dispatch_data_t region, size_t offset, const void *buffer, size_t size) -> bool {
ret += 1;
return true;
});
return ret;
}
inline operator dispatch_data_t() const {
return _data;
}
inline Data &operator = (const dispatch_data_t &other) {
if (other) {
sr_dispatch_retain(other);
}
if (_data) {
sr_dispatch_release(_data);
}
_data = other;
return *this;
}
inline Data &operator += (const Data &other) {
return (*this = Concat(other));
}
virtual ~Data() {
if (_data) {
sr_dispatch_release(_data);
}
}
static const Data empty;
};
}
}
#endif /* defined(__SocketRocket__DispatchData__) */

View File

@ -0,0 +1,15 @@
//
// DispatchData.cpp
// SocketRocket
//
// Created by Michael Lewis on 1/12/13.
//
//
#include "DispatchData.h"
namespace squareup {
namespace dispatch {
const Data Data::empty(dispatch_data_empty);
}
}

91
SocketRocket/DispatchIO.h Normal file
View File

@ -0,0 +1,91 @@
//
// DispatchChannel.h
// SocketRocket
//
// Created by Michael Lewis on 1/12/13.
//
//
#ifndef __SocketRocket__DispatchChannel__
#define __SocketRocket__DispatchChannel__
#include <dispatch/dispatch.h>
#include <string>
extern "C" {
#include <netdb.h>
}
namespace squareup {
namespace dispatch {
class RawIO;
typedef void (^dial_callback)(dispatch_fd_t fd, int error_code, const char *error_message);
void Dial(dispatch_queue_t workQueue, const char *hostname, const char *servname, dispatch_queue_t callback_queue, dial_callback callback);
// You are responsible for removing inputStream and outputStream
typedef void (^simple_dial_callback)(RawIO *io, int error, const char *error_message);
// callback_queue is what is sent to the clients but also
// parent_io_queue can be a parallel queue. the
// close_handler is passed to the RawIO
void SimpleDial(const char *hostname, const char *servname, dispatch_queue_t callback_queue, dispatch_queue_t parent_io_queue, simple_dial_callback dial_callback, void(^close_handler)(int error) = nullptr);
class IO {
public:
virtual void Close(dispatch_io_close_flags_t flags) = 0;
virtual void Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler) = 0;
virtual void Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler) = 0;
virtual void Barrier(dispatch_block_t barrier) = 0;
virtual void SetHighWater(size_t high_water) = 0;
virtual void SetLowWater(size_t low_water) = 0;
virtual ~IO();
};
class RawIO : public IO {
dispatch_io_t _channel;
public:
void Close(dispatch_io_close_flags_t flags);
void Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler);
void Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler);
void Barrier(dispatch_block_t barrier);
void SetHighWater(size_t high_water);
void SetLowWater(size_t low_water);
RawIO(dispatch_fd_t fd,
dispatch_queue_t cleanupQueue,
dispatch_queue_t callbackQueue,
dispatch_queue_t ioQueue,
void (^cleanup_handler)(int error));
// Takes ownership of channel
// retain is only for the channel
RawIO(dispatch_io_t channel,
dispatch_queue_t callbackQueue,
bool retain = true);
// Clones an existing IO
RawIO(dispatch_io_t otherIo,
dispatch_queue_t queue,
dispatch_queue_t callbackQueue,
dispatch_queue_t ioQueue,
void (^cleanup_handler)(int error));
inline operator dispatch_io_t () const {
return _channel;
}
virtual ~RawIO();
};
}
}
#endif /* defined(__SocketRocket__DispatchChannel__) */

298
SocketRocket/DispatchIO.mm Normal file
View File

@ -0,0 +1,298 @@
//
// DispatchChannel.cpp
// SocketRocket
//
// Created by Michael Lewis on 1/12/13.
//
//
#include "DispatchIO.h"
#include <Block.h>
#include <string>
#include "Common.h"
extern "C" {
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
}
// TODO: add deadlines
namespace squareup {
namespace dispatch {
IO::~IO() {};
class Connector;
typedef void(^finish_callback)(Connector *connector, addrinfo *res0, dispatch_fd_t fd, int error_code, const char *error_message);
class Connector {
addrinfo *_res;
addrinfo *_res0;
__strong finish_callback _finishCallback;
dispatch_queue_t _workQueue;
int _lastError;
const char *_lastErrorMessage;
bool _foundAddr = false;
public:
// Takes ownership of res0;
inline Connector(dispatch_queue_t workQueue, struct addrinfo *res0, finish_callback finishCallback) : _res(res0), _res0(res0), _finishCallback([finishCallback copy]), _workQueue(workQueue) {
sr_dispatch_retain(_workQueue);
}
virtual ~Connector() {
sr_dispatch_release(_workQueue);
}
inline void NextIter() {
_res = _res->ai_next;
dispatch_async(_workQueue, ^{
DoNext();
});
}
inline void DoNext() {
// We ran out of addresses
if (!_res) {
assert(!_foundAddr);
_finishCallback(this, _res0, -1, _lastError, _lastErrorMessage);
_finishCallback = nullptr;
return;
}
dispatch_fd_t fd = socket(_res->ai_family, _res->ai_socktype,
_res->ai_protocol);
if (fd < 0) {
_lastError = errno;
_lastErrorMessage = strerror(_lastError);
NextIter();
return;
}
int curflags = fcntl(fd, F_GETFL);
if (curflags < 0) {
_lastError = errno;
_lastErrorMessage = strerror(_lastError);
NextIter();
return;
}
// Set it to be nonblocking
int error = fcntl(fd, F_SETFL, curflags | O_NONBLOCK);
if (error) {
_lastError = errno;
_lastErrorMessage = strerror(_lastError);
NextIter();
return;
}
dispatch_source_t readSource = nullptr;
readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, _workQueue);
dispatch_resume(readSource);
dispatch_source_set_event_handler(readSource, [this, fd, readSource]{
TryConnect(false, fd, readSource);
});
dispatch_source_set_cancel_handler(readSource, [fd]{
close(fd);
});
TryConnect(true, fd, readSource);
}
private:
// returns true if it is done
// if this is the first try we call connect. otherwise we check the status
inline void TryConnect(bool firstTry, dispatch_fd_t fd, dispatch_source_t readSource) {
// Now, set connection to be non-blocking
assert(!_foundAddr);
assert(fd != -1);
_lastError = 0;
if (firstTry) {
int error = connect(fd, _res->ai_addr, _res->ai_addrlen);
if (error != 0) {
_lastError = errno;
}
} else {
socklen_t len = sizeof(_lastError);
int sockErr = getsockopt(fd, SOL_SOCKET, SO_ERROR, &_lastError, &len);
assert(sockErr == 0);
}
if (_lastError != 0) {
if (_lastError != EINPROGRESS) {
_lastErrorMessage = strerror(_lastError);
dispatch_source_cancel(readSource);
sr_dispatch_release(readSource);
readSource = nullptr;
NextIter();
}
return;
}
_foundAddr = true;
// We don't want to close the FD, so make the cancel handler a noop
dispatch_suspend(readSource);
dispatch_source_set_cancel_handler(readSource, ^{});
dispatch_resume(readSource);
dispatch_source_cancel(readSource);
// Dispose of it without canceling it
sr_dispatch_release(readSource);
// Successful connections get here. Then we don't try anymore
// If we get this far, we're done
dispatch_async(_workQueue, [this, fd]{
_finishCallback(this, _res0, fd, 0, nullptr);
_finishCallback = nullptr;
});
return;
}
};
void Dial(dispatch_queue_t workQueue, const char *hostname, const char *servname, dispatch_queue_t callback_queue, dial_callback callback) {
callback = [callback copy];
sr_dispatch_retain(callback_queue);
// Does cleanup and whatnot
dispatch_async(workQueue, ^{
addrinfo *res0 = nullptr;
int error;
const char *cause = nullptr;
struct addrinfo hints = {0};
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(hostname, servname, &hints, &res0);
auto finish = [callback_queue, callback, res0, workQueue](Connector *connector, addrinfo *res0, dispatch_fd_t fd, int error_code, const char *error_message){
NSLog(@"Pew");
dispatch_async(callback_queue, [fd, callback_queue, workQueue, error_message, callback, res0, connector, error_code]{
callback(fd, error_code, error_message);
if (res0) {
freeaddrinfo(res0);
}
if (connector) {
// Delete it after it doesn't reference this block anymore
dispatch_async(callback_queue, [workQueue, connector, callback_queue]{
delete connector;
sr_dispatch_release(callback_queue);
});
} else {
sr_dispatch_release(callback_queue);
}
});
};
if (error) {
cause = gai_strerror(error);
finish(nullptr, res0, -1, error, cause);
return;
}
Connector *connector = new Connector(workQueue, res0, finish);
connector->DoNext();
});
}
// callback_queue is what is sent to the clients but also
// parent_io_queue can be a parallel queue. the
void SimpleDial(const char *hostname, const char *servname, dispatch_queue_t callback_queue, dispatch_queue_t parent_io_queue, simple_dial_callback dial_callback, void (^close_handler)(int error)) {
dispatch_queue_t io_queue = dispatch_queue_create("squareup.dispatch.SimpleDial IO dispatch_queue_t", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(io_queue, parent_io_queue);
sr_dispatch_retain(callback_queue);
if (close_handler != nullptr) {
close_handler = [close_handler copy];
}
Dial(callback_queue, hostname, servname, callback_queue, [=](dispatch_fd_t fd, int error_code, const char *error_message) {
RawIO *io = nullptr;
if (error_code == 0) {
// Going to make the writer the primary.
// The write stream is also appropriate for closing
io = new RawIO(fd, callback_queue, callback_queue, io_queue, [fd, close_handler](int error) {
close(fd);
if (close_handler != nullptr) {
close_handler(error);
}
});
}
dial_callback(io, error_code, error_message);
sr_dispatch_release(io_queue);
sr_dispatch_release(parent_io_queue);
});
}
RawIO::RawIO(dispatch_fd_t fd,
dispatch_queue_t cleanupQueue,
dispatch_queue_t callbackQueue,
dispatch_queue_t ioQueue,
void (^cleanup_handler)(int error)) {
_channel = dispatch_io_create(DISPATCH_IO_STREAM, fd, cleanupQueue, cleanup_handler);
dispatch_set_target_queue(_channel, ioQueue);
}
// Takes ownership of channel
// retain is only for the channel
RawIO::RawIO(dispatch_io_t channel, dispatch_queue_t callbackQueue, bool retain) : _channel(channel) {
if (retain) {
sr_dispatch_retain(_channel);
}
};
// Clones an existing IO
RawIO::RawIO(dispatch_io_t otherIo, dispatch_queue_t queue, dispatch_queue_t callbackQueue, dispatch_queue_t ioQueue, void (^cleanup_handler)(int error)) :
RawIO(dispatch_io_create_with_io(DISPATCH_IO_STREAM, otherIo, queue, cleanup_handler), callbackQueue, false) {
dispatch_set_target_queue(_channel, ioQueue);
}
RawIO::~RawIO() {
sr_dispatch_release(_channel);
_channel = nullptr;
}
void RawIO::Close(dispatch_io_close_flags_t flags) {
dispatch_io_close(_channel, flags);
}
void RawIO::Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler) {
dispatch_io_read(_channel, 0, length, queue, handler);
}
void RawIO::Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler) {
dispatch_io_write(_channel, 0, data, queue, handler);
}
void RawIO::Barrier(dispatch_block_t barrier) {
dispatch_io_barrier(_channel, barrier);
}
void RawIO::SetHighWater(size_t high_water) {
dispatch_io_set_high_water(_channel, high_water);
}
void RawIO::SetLowWater(size_t low_water) {
dispatch_io_set_low_water(_channel, low_water);
}
}
}

26
SocketRocket/Info.plist Normal file
View File

@ -0,0 +1,26 @@
<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -13,7 +13,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
extern "C" {
#import "SRWebSocket.h"
@ -34,20 +34,13 @@
#import <CommonCrypto/CommonDigest.h>
#import <Security/SecRandom.h>
#if OS_OBJECT_USE_OBJC_RETAIN_RELEASE
#define sr_dispatch_retain(x)
#define sr_dispatch_release(x)
#define maybe_bridge(x) ((__bridge void *) x)
#else
#define sr_dispatch_retain(x) dispatch_retain(x)
#define sr_dispatch_release(x) dispatch_release(x)
#define maybe_bridge(x) (x)
#endif
#import "Common.h"
#if !__has_feature(objc_arc)
#error SocketRocket must be compiled with ARC enabled
#endif
}
typedef enum {
SROpCodeTextFrame = 0x1,
@ -104,7 +97,7 @@ static inline void SRFastLog(NSString *format, ...);
@end
static NSString *newSHA1String(const char *bytes, size_t length) {
static NSString *newSHA1String(const void *bytes, size_t length) {
uint8_t md[CC_SHA1_DIGEST_LENGTH];
assert(length >= 0);
@ -305,7 +298,7 @@ static __strong NSData *CRLFCRLF;
_workQueue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);
// Going to set a specific on the queue so we can validate we're on the work queue
dispatch_queue_set_specific(_workQueue, (__bridge void *)self, maybe_bridge(_workQueue), NULL);
dispatch_queue_set_specific(_workQueue, (__bridge void *)self, (__sr_maybe_bridge__ void *)(_workQueue), NULL);
_delegateDispatchQueue = dispatch_get_main_queue();
sr_dispatch_retain(_delegateDispatchQueue);
@ -328,7 +321,7 @@ static __strong NSData *CRLFCRLF;
- (void)assertOnWorkQueue;
{
assert(dispatch_get_specific((__bridge void *)self) == maybe_bridge(_workQueue));
assert(dispatch_get_specific((__bridge void *)self) == (__sr_maybe_bridge__ void*)(_workQueue));
}
- (void)dealloc
@ -480,7 +473,7 @@ static __strong NSData *CRLFCRLF;
CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Host"), (__bridge CFStringRef)(_url.port ? [NSString stringWithFormat:@"%@:%@", _url.host, _url.port] : _url.host));
NSMutableData *keyBytes = [[NSMutableData alloc] initWithLength:16];
SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, keyBytes.mutableBytes);
SecRandomCopyBytes(kSecRandomDefault, keyBytes.length, (uint8_t *)keyBytes.mutableBytes);
if ([keyBytes respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
_secKey = [keyBytes base64EncodedStringWithOptions:0];
@ -957,7 +950,7 @@ static const uint8_t SRPayloadLenMask = 0x7F;
[self _addConsumerWithDataLength:2 callback:^(SRWebSocket *self, NSData *data) {
__block frame_header header = {0};
const uint8_t *headerBuffer = data.bytes;
const uint8_t *headerBuffer = reinterpret_cast<const uint8_t *>(data.bytes);
assert(data.length >= 2);
if (headerBuffer[0] & SRRsvMask) {
@ -1054,7 +1047,7 @@ static const uint8_t SRPayloadLenMask = 0x7F;
NSUInteger dataLength = _outputBuffer.length;
if (dataLength - _outputBufferOffset > 0 && _outputStream.hasSpaceAvailable) {
NSInteger bytesWritten = [_outputStream write:_outputBuffer.bytes + _outputBufferOffset maxLength:dataLength - _outputBufferOffset];
NSInteger bytesWritten = [_outputStream write:reinterpret_cast<const uint8_t *>(_outputBuffer.bytes) + _outputBufferOffset maxLength:dataLength - _outputBufferOffset];
if (bytesWritten == -1) {
[self _failWithError:[NSError errorWithDomain:SRWebSocketErrorDomain code:2145 userInfo:[NSDictionary dictionaryWithObject:@"Error writing to stream" forKey:NSLocalizedDescriptionKey]]];
return;
@ -1063,7 +1056,7 @@ static const uint8_t SRPayloadLenMask = 0x7F;
_outputBufferOffset += bytesWritten;
if (_outputBufferOffset > 4096 && _outputBufferOffset > (_outputBuffer.length >> 1)) {
_outputBuffer = [[NSMutableData alloc] initWithBytes:(char *)_outputBuffer.bytes + _outputBufferOffset length:_outputBuffer.length - _outputBufferOffset];
_outputBuffer = [[NSMutableData alloc] initWithBytes:reinterpret_cast<const void *>(reinterpret_cast<const char *>(_outputBuffer.bytes) + _outputBufferOffset) length:_outputBuffer.length - _outputBufferOffset];
_outputBufferOffset = 0;
}
}
@ -1133,7 +1126,7 @@ static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
__block size_t match_count = 0;
size_t size = data.length;
const unsigned char *buffer = data.bytes;
const uint8_t *buffer = reinterpret_cast<const uint8_t *>(data.bytes);
for (size_t i = 0; i < size; i++ ) {
if (((const unsigned char *)buffer)[i] == ((const unsigned char *)bytes)[match_count]) {
match_count += 1;
@ -1201,7 +1194,7 @@ static const char CRLFCRLFBytes[] = {'\r', '\n', '\r', '\n'};
NSMutableData *mutableSlice = [slice mutableCopy];
NSUInteger len = mutableSlice.length;
uint8_t *bytes = mutableSlice.mutableBytes;
uint8_t *bytes = reinterpret_cast<uint8_t *>(mutableSlice.mutableBytes);
for (NSUInteger i = 0; i < len; i++) {
bytes[i] = bytes[i] ^ _currentReadMaskKey[_currentReadMaskOffset % sizeof(_currentReadMaskKey)];

File diff suppressed because it is too large Load Diff

176
SocketRocket/SecureIO.h Normal file
View File

@ -0,0 +1,176 @@
//
// SecureIO.h
// SocketRocket
//
// Created by Michael Lewis on 1/12/13.
//
//
#ifndef __SocketRocket__SecureIO__
#define __SocketRocket__SecureIO__
#include "DispatchIO.h"
#include "DispatchData.h"
#include <deque>
#include <vector>
struct SSLContext;
namespace squareup {
namespace dispatch {
class SecureIO;
// You are responsible for removing inputStream and outputStream
typedef void (^dial_tls_callback)(SecureIO *io, int error, const char *error_message);
void DialTLS(const char *hostname, const char *servname, SSLContextRef ssl_context, dispatch_queue_t callback_queue, dispatch_queue_t work_queue, dispatch_queue_t parent_io_queue, dial_tls_callback dial_callback, void(^close_handler)(int error) = nullptr);
template <typename FuncType>
class QueuedHandle {
private:
__strong FuncType _handler = nullptr;
__sr_maybe_strong__ dispatch_queue_t _queue = nullptr;
public:
inline QueuedHandle() {
}
inline QueuedHandle(dispatch_queue_t queue, FuncType handler) {
_handler = [handler copy];
_queue = queue;
sr_dispatch_retain(_queue);
}
inline QueuedHandle(const QueuedHandle<FuncType> &other) : QueuedHandle(other._queue, other._handler){
}
inline QueuedHandle &operator=(const QueuedHandle &other){
_handler = other._handler;
_queue = other._queue;
sr_dispatch_release(_queue);
return *this;
}
inline ~QueuedHandle() {
if (_queue) {
sr_dispatch_release(_queue);
}
}
inline bool Valid() const {
return _handler != nullptr;
}
inline void Invalidate() {
_handler = nullptr;
}
template<typename... Args>
void operator () (Args... args) const {
assert(Valid());
FuncType handler = _handler;
dispatch_async(_queue, [handler, args...]{
handler(args...);
});
}
};
typedef QueuedHandle<dispatch_io_handler_t> DispatchHandler;
struct WriteJob {
bool isLast;
size_t rawBytes = 0;
size_t cryptedBytes = 0;
DispatchHandler handler;
};
struct ReadRequest {
// How many rawBytes we're expecting to read.
// This is populated in SSLRead.
size_t rawBytesRemaining = 0;
DispatchHandler handler;
};
class SecureIO : public IO {
IO *_io;
SSLContext *_context;
dispatch_queue_t _workQueue;
std::deque<WriteJob> _writeJobs;
std::deque<ReadRequest> _readRequests;
Data _waitingCryptedData;
size_t _cryptedBytesRequested = 0;
size_t _rawBytesRequested = 0;
size_t _highWater = SIZE_MAX;
size_t _lowWater = 1024 * 8;
bool _cancelled = false;
bool _closing = false;
// Set when we're inside of HandleSSLRead
// If we're reading and get a call to our SSLWriteHandler, the connection is probably being closed
bool _handlingRead = false;
// presence of this means handshake is in progress
DispatchHandler _handshakeHandler;
// This is set to true when we send a NULL data ptr. This is so we can request data we need.
bool _calculatingRequestSize = false;
std::vector<uint8_t> _sslReadBuffer;
public:
// Takes ownership of IO and delete it when done
SecureIO(IO *io, SSLContextRef context, dispatch_queue_t workQueue);
~SecureIO();
// This performs the handshake.
// There will be no data sent back to handler, but it will be called on completion
void Handshake(dispatch_queue_t queue, dispatch_io_handler_t handler);
void Close(dispatch_io_close_flags_t flags);
void Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler);
void Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler);
void Barrier(dispatch_block_t barrier);
inline void SetHighWater(size_t high_water) {
_highWater = high_water;
}
void SetLowWater(size_t low_water) {
_lowWater = low_water;
}
private:
// Requests will ask the underlying stream for lenght - _rawBytesRequested
void RequestBytes(size_t length);
void Cancel(dispatch_io_close_flags_t flags, int error);
void CheckHandshake();
void InnerWrite(dispatch_data_t data, const DispatchHandler &handler);
// These really should be private
public:
OSStatus SSLReadHandler(void *data, size_t *dataLength);
OSStatus SSLWriteHandler(const void *data, size_t *dataLength);
private:
void HandleSSLWrite(bool done, size_t requestedLength, int error);
void HandleSSLRead(bool done, dispatch_data_t data, int error);
void PumpSSLRead();
void DoSSLRead();
};
}
}
#endif /* defined(__SocketRocket__SecureIO__) */

475
SocketRocket/SecureIO.mm Normal file
View File

@ -0,0 +1,475 @@
//
// SecureIO.cpp
// SocketRocket
//
// Created by Michael Lewis on 1/12/13.
//
//
#include <Security/SecureTransport.h>
#include "SecureIO.h"
#include "DispatchData.h"
#define ALLOW_INSECURE_SSL 1
namespace squareup {
namespace dispatch {
static OSStatus readFunc(SSLConnectionRef connection, void *data, size_t *dataLength);
static OSStatus writeFunc(SSLConnectionRef connection, const void *data, size_t *dataLength);
void DialTLS(const char *hostname,
const char *servname,
SSLContextRef ssl_context,
dispatch_queue_t callback_queue,
dispatch_queue_t work_queue,
dispatch_queue_t parent_io_queue,
dial_tls_callback dial_callback,
void(^close_handler)(int error)) {
if (close_handler != nullptr) {
close_handler = [close_handler copy];
}
dial_callback = [dial_callback copy];
sr_dispatch_retain(callback_queue);
// The work queue is the outer ones callback_queue
SimpleDial(hostname, servname, work_queue, parent_io_queue, [=](squareup::dispatch::RawIO *io, int error, const char *error_message) {
if (error != 0 || io == nullptr) {
dispatch_async(callback_queue, ^{
dial_callback(nullptr, error, error_message);
});
sr_dispatch_release(callback_queue);
return;
}
SecureIO *newIO = new SecureIO(io, ssl_context, work_queue);
sr_dispatch_release(callback_queue);
newIO->Handshake(callback_queue, [newIO, dial_callback](bool done, dispatch_data_t data, int error) {
SecureIO *io = newIO;
if (error) {
delete io;
io = nullptr;
}
// TODO: maybe add message?
dial_callback(io, error, nullptr);
});
}, close_handler);
}
SecureIO::SecureIO(IO *io, SSLContextRef context, dispatch_queue_t workQueue) :
_io(io), _context(context), _workQueue(workQueue) {
sr_dispatch_retain(_workQueue);
CFRetain(_context);
SSLSetConnection(_context, reinterpret_cast<const void *>(this));
SSLSetIOFuncs(_context, readFunc, writeFunc);
// TODO: delegate certificate authentication
#if ALLOW_INSECURE_SSL
SSLSetSessionOption(_context, kSSLSessionOptionBreakOnServerAuth, true);
#endif
}
SecureIO::~SecureIO() {
sr_dispatch_release(_workQueue);
CFRelease(_context);
// make sure things closed before we delete the io
assert(!_io);
}
void SecureIO::Handshake(dispatch_queue_t queue, dispatch_io_handler_t handler) {
_handshakeHandler = DispatchHandler(queue, handler);
dispatch_async(_workQueue, [this]{
CheckHandshake();
});
}
void SecureIO::Close(dispatch_io_close_flags_t flags) {
assert(!_closing);
dispatch_async(_workQueue, [this, flags]{
assert(!_closing);
_closing = true;
OSStatus status = SSLClose(_context);
// This shouldn't be a blocking operation
assert(status != errSSLWouldBlock);
Cancel(flags, status);
return;
});
}
void SecureIO::Read(size_t length, dispatch_queue_t queue, dispatch_io_handler_t handler) {
DispatchHandler dispatchHandler(queue, [handler copy]);
dispatch_async(_workQueue, [length, dispatchHandler, this]{
if (_cancelled) {
dispatchHandler(true, (dispatch_data_t)nullptr, ECANCELED);
return;
}
ReadRequest readRequest;
readRequest.handler = dispatchHandler;
readRequest.rawBytesRemaining += length;
_readRequests.push_back(readRequest);
// TODO: run on queue
_rawBytesRequested += length;
PumpSSLRead();
});
}
void SecureIO::PumpSSLRead() {
size_t buffSize = SIZE_MAX;
// This can happen if we have leftover data and haven't requested more.
while (buffSize > 0) {
// Make sure we are requesting enough;
OSStatus status = SSLGetBufferedReadSize(_context, &buffSize);
assert(status == 0);
if (buffSize > 0) {
if (_readRequests.size() == 0) {
return;
}
DoSSLRead();
}
}
_calculatingRequestSize = true;
const size_t maxBufferSize = 32 * 1024;
// We don't actually want to read anything into the _sslReadBuffer, howevr there's a bug when we go through 2g of data we have a memory issue
_sslReadBuffer.resize(std::max(_sslReadBuffer.capacity(), std::min(_rawBytesRequested, maxBufferSize)));
size_t dummyProcessed;
assert(buffSize == 0);
OSStatus status = ::SSLRead(_context, _sslReadBuffer.data(), _sslReadBuffer.size(), &dummyProcessed);
assert(dummyProcessed == 0);
_calculatingRequestSize = false;
if (status == errSSLClosedGraceful) {
if (!_closing) {
_closing = true;
Cancel(0, 0);
return;
}
}
assert(status == errSSLWouldBlock);
}
void SecureIO::Write(dispatch_data_t data, dispatch_queue_t queue, dispatch_io_handler_t handler) {
sr_dispatch_retain(data);
DispatchHandler dispatchHandler(queue, handler);
Data(data).Apply(^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) {
return true;
});
dispatch_async(_workQueue, [data, dispatchHandler, this]{
InnerWrite(data, dispatchHandler);
sr_dispatch_release(data);
});
}
void SecureIO::InnerWrite(dispatch_data_t data, const DispatchHandler &handler) {
if (_cancelled) {
handler(true, (dispatch_data_t)nullptr, ECANCELED);
return;
}
OSStatus result = 0;
size_t totalSize = dispatch_data_get_size(data);
dispatch_data_apply(data, [&](dispatch_data_t region, size_t offset, const void *buffer, size_t size) -> bool {
WriteJob newJob;
newJob.handler = handler;
newJob.rawBytes = size;
newJob.isLast = offset + size == totalSize;
newJob.cryptedBytes = 0;
_writeJobs.push_back(newJob);
// WriteSSL will fill in the crypted bytes
// isLast will be set to True for the last one
size_t sizeWritten = 0;
result = ::SSLWrite(_context, buffer, size, &sizeWritten);
// I think we can make this assumption since we don't block at all in our handler.
if (result != 0) {
return false;
};
// We should be able to make this assumptions since our write func never blocks
assert(sizeWritten == size);
return true;
});
if (result) {
Cancel(0, result);
return;
}
}
void SecureIO::RequestBytes(size_t bytesWanted) {
if (bytesWanted > _cryptedBytesRequested) {
size_t requestSize = bytesWanted - _cryptedBytesRequested;
_cryptedBytesRequested += requestSize;
_io->Read(requestSize, _workQueue, [this](bool done, dispatch_data_t data, int error) {
HandleSSLRead(done, data, error);
});
}
};
OSStatus SecureIO::SSLReadHandler(void *data, size_t *dataLength) {
if (_calculatingRequestSize) {
RequestBytes(*dataLength);
*dataLength = 0;
return errSSLWouldBlock;
}
size_t bytesRequested = *dataLength;
// If _calculatingRequestSize is true, always return errSSLWouldBlock and pretend we don't have any data
size_t bytesToCopy = std::min(_waitingCryptedData.Size(), bytesRequested);
*dataLength = bytesToCopy;
if (bytesToCopy > 0) {
// Advance it forward
_waitingCryptedData = _waitingCryptedData.TakeInto(bytesToCopy, data);
_waitingCryptedData.FlattenIfNecessary();
}
if (bytesToCopy < bytesRequested) {
RequestBytes(bytesRequested - bytesToCopy);
return errSSLWouldBlock;
}
return noErr;
}
OSStatus SecureIO::SSLWriteHandler(const void *data, size_t *dataLength) {
size_t requestedLength = *dataLength;
if (!_handshakeHandler.Valid() && !_closing && !_handlingRead) {
assert(_writeJobs.size() > 0);
_writeJobs.back().cryptedBytes += requestedLength;
}
_io->Write(Data(data, requestedLength, _workQueue), _workQueue, [this, requestedLength](bool done, dispatch_data_t data, int error) {
HandleSSLWrite(done, requestedLength, error);
});
return 0;
}
void SecureIO::HandleSSLRead(bool done, dispatch_data_t data, int error) {
if (!_handshakeHandler.Valid() && _readRequests.size() == 0) {
return;
}
if (error != 0) {
if (_handshakeHandler.Valid()) {
_handshakeHandler(done, (dispatch_data_t)nullptr, error);
_handshakeHandler.Invalidate();
} else {
assert(_readRequests.size() > 0);
Cancel(0, error);
}
return;
}
// We got some data, so append it
Data d(data);
_cryptedBytesRequested -= d.Size();
_waitingCryptedData += d;
if (_handshakeHandler.Valid()) {
CheckHandshake();
return;
}
DoSSLRead();
PumpSSLRead();
}
void SecureIO::DoSSLRead() {
assert(_readRequests.size() > 0);
ReadRequest *frontRead = &_readRequests.front();
const DispatchHandler &handler = frontRead->handler;
size_t length = frontRead->rawBytesRemaining;
// Let's cap length at 2x the bytes we have available. probably won't use all of it (it will probably be less than 1x)
length = std::min(length, 2 * _waitingCryptedData.Size());
length = std::min(length, _highWater);
size_t buffSize = 0;
SSLGetBufferedReadSize(_context, &buffSize);
length = std::max(buffSize, length);
size_t sizeRead = 0;
void *buffer = malloc(length);
assert(buffer);
// TODO: optimize this and not malloc memory each time
assert(_calculatingRequestSize == false);
assert(_handlingRead == false);
_handlingRead = true;
OSStatus status = ::SSLRead(_context, buffer, length, &sizeRead);
_handlingRead = false;
if (status != 0 && status != errSSLWouldBlock && status != errSSLClosedGraceful) {
free(buffer);
buffer = nullptr;
// TODO: handle error better
Cancel(0, status);
return;
}
Data rawData(dispatch_data_create(buffer, sizeRead, _workQueue, DISPATCH_DATA_DESTRUCTOR_FREE), false);
frontRead->rawBytesRemaining -= sizeRead;
bool isDone = (frontRead->rawBytesRemaining == 0);
// TODO: honor watermarks
handler(isDone, rawData, 0);
if (status == errSSLClosedGraceful) {
// TODO: handle close
}
if (isDone) {
_readRequests.pop_front();
}
}
void SecureIO::HandleSSLWrite(bool done, size_t requestedLength, int error) {
if (_handshakeHandler.Valid()) {
CheckHandshake();
return;
}
if (_closing) {
return;
}
assert(_writeJobs.size() > 0);
// TODO(lewis): handle rest of errors better
if (error != 0) {
_writeJobs.front().handler(true, dispatch_data_empty, error);
return;
}
assert(_writeJobs.size());
// We only want to call the handlers when we are "done".
// This way we can approximate the bytes that are written
if (done) {
// if we're an error we want to go to the last one
if (error) {
Cancel(0, error);
return;
}
assert(_writeJobs.size());
_writeJobs.front().cryptedBytes -= requestedLength;
WriteJob writeJob = _writeJobs.front();
// Only call them when we're "done" for now, because we don't want to do bookkeeping of remaining data to consume
// TODO: probably change this
if (writeJob.cryptedBytes == 0) {
_writeJobs.pop_front();
if (writeJob.isLast) {
writeJob.handler(true, dispatch_data_empty, error);
}
}
}
}
void SecureIO::CheckHandshake() {
assert(_handshakeHandler.Valid());
OSStatus status = ::SSLHandshake(_context);
// If it would block we got nothing to do
if (status == errSSLWouldBlock) {
return;
}
#if ALLOW_INSECURE_SSL
// TODO: make this better
if (status == errSSLPeerAuthCompleted) {
CheckHandshake();
return;
}
#endif
_handshakeHandler(true, (dispatch_data_t)nullptr, status);
_handshakeHandler.Invalidate();
}
void SecureIO::Cancel(dispatch_io_close_flags_t flags, int error) {
_cancelled = true;
for (const ReadRequest &req : _readRequests) {
req.handler(true, (dispatch_data_t)nullptr, ECANCELED);
}
for (const WriteJob &job : _writeJobs) {
job.handler(true, (dispatch_data_t)nullptr, ECANCELED);
}
_readRequests.clear();
_writeJobs.clear();
_io->Close(flags);
delete _io;
_io = nullptr;
}
void SecureIO::Barrier(dispatch_block_t barrier) {
_io->Barrier(barrier);
}
OSStatus readFunc(SSLConnectionRef connection, void *data, size_t *dataLength) {
return reinterpret_cast<SecureIO *>((void *)connection)->SSLReadHandler(data, dataLength);
};
OSStatus writeFunc(SSLConnectionRef connection, const void *data, size_t *dataLength) {
return reinterpret_cast<SecureIO *>((void *)connection)->SSLWriteHandler(data, dataLength);
};
}
}

View File

@ -0,0 +1,19 @@
//
// SocketRocket.h
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
#import <UIKit/UIKit.h>
//! Project version number for SocketRocket.
FOUNDATION_EXPORT double SocketRocketVersionNumber;
//! Project version string for SocketRocket.
FOUNDATION_EXPORT const unsigned char SocketRocketVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <SocketRocket/PublicHeader.h>

View File

@ -0,0 +1,10 @@
////
//// WebSocket.swift
//// SocketRocket
////
//// Created by Mike Lewis on 1/6/16.
////
////
import Foundation
import RxSwift

View File

@ -0,0 +1,299 @@
//
// WebSocketProtocol.swift
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
import Foundation
import RxSwift
import SocketRocketIO
public protocol MessageOutputStream {
/// This is the raw way to write messages. This probably shouldn't be used by general implementations (it is unsafe). By using unsafeBufferPointer we can avoid most copying of data
func writeMessageRaw(buffers: Observable<UnsafeBufferPointer<UInt8>>)
}
public protocol MessageInputStream {
@warn_unused_result
func readMessagesRaw(scheduler: ImmediateSchedulerType) -> Observable<Observable<UnsafeBufferPointer<UInt8>>>
}
private let controlFrameRange = 0x8..<0xF
func == (lhs: WebSocketFrameHeader, rhs: WebSocketFrameHeader) -> Bool {
return lhs.fin == rhs.fin
&& lhs.opcode == rhs.opcode
&& lhs.mask == rhs.mask
&& lhs.payloadLen == rhs.payloadLen
&& lhs.maskingKey == rhs.maskingKey
}
struct WebSocketFrameHeader : Equatable {
let fin: Bool
let opcode: Int // We're not using normal opcodes for this incase they're undefined
let mask: Bool
let payloadLen: Int
let maskingKey: UInt32?
static let initialBytesNeeded = 2
/// Constructs a complete frame
/// Mask is inferred based on maskingKey
init(fin: Bool, opcode: WebSocketOpcode, payloadLen: Int, maskingKey: UInt32? = nil) {
self.fin = fin
self.mask = maskingKey != nil
self.maskingKey = maskingKey
self.payloadLen = payloadLen
self.opcode = opcode.rawValue
}
/// Whether or not we're just the first part of a frame
var isComplete: Bool {
if mask && self.maskingKey == nil {
return false
}
if payloadLen == 126 || payloadLen == 127 {
return false
}
return true
}
/// Initializes from 16 bits of data.
init(data: UnsafeBufferPointer<UInt8>) {
precondition(data.count == WebSocketFrameHeader.initialBytesNeeded, "Must only initialize with 16 bits of data")
let firstByte = data[data.startIndex]
let secondByte = data[data.startIndex.advancedBy(1)]
fin = firstByte & 0b1000_0000 != 0
opcode = Int(firstByte & 0b0000_1111)
mask = secondByte & 0b1000_0000 != 0
payloadLen = Int(secondByte & 0b0111_1111)
maskingKey = nil
}
/// Constructor for creating a websocket with additional data
init(original: WebSocketFrameHeader, additionalData: UnsafeBufferPointer<UInt8>) {
precondition(original.additionalBytesNeeded == additionalData.count)
self.fin = original.fin
self.opcode = original.opcode
self.mask = original.mask
let maskOffset: Int
// Now we have to read the payload length
switch original.payloadLen {
case 126:
var networkOrderLen: UInt16 = 0
let lenSize = sizeofValue(networkOrderLen)
memcpy(&networkOrderLen, additionalData.baseAddress, lenSize)
payloadLen = Int(networkOrderLen.bigEndian)
maskOffset = lenSize
case 127:
var networkOrderLen: UInt64 = 0
let lenSize = sizeofValue(networkOrderLen)
memcpy(&networkOrderLen, additionalData.baseAddress, lenSize)
payloadLen = Int(networkOrderLen.bigEndian)
maskOffset = lenSize
default:
payloadLen = original.payloadLen
maskOffset = 0
}
/// If there's a mask we need to read that
if mask {
let startPtr = additionalData.baseAddress.advancedBy(maskOffset)
var key: UInt32 = 0
memcpy(&key, startPtr, sizeofValue(key))
maskingKey = key
} else {
maskingKey = nil
}
}
/// These are additional bytes we need to complete the frame. This accounts of additional
var additionalBytesNeeded: Int {
let extendedPayloadNeeded: Int
switch self.payloadLen {
case 126:
extendedPayloadNeeded = 2
case 127:
extendedPayloadNeeded = 8
default:
extendedPayloadNeeded = 0
}
let maskingLengthNeeded = mask ? sizeof(UInt32) : 0
return extendedPayloadNeeded + maskingLengthNeeded
}
var isControlFrame : Bool {
return controlFrameRange.contains(opcode)
}
}
public struct WebSocketFrame {
let header: WebSocketFrameHeader
let payload: Observable<UnsafeBufferPointer<UInt8>>
}
public struct WebSocketFrameReader<I: InputStream where I.Element == UInt8> {
let stream: I
public init(stream: I) {
self.stream = stream
}
@warn_unused_result
private func readFrameHeader() -> Observable<WebSocketFrameHeader> {
return self.stream
.readAndBufferExactly(WebSocketFrameHeader.initialBytesNeeded, allowEmpty: true)
.map { WebSocketFrameHeader(data: $0) }
.flatMap { header -> Observable<WebSocketFrameHeader> in
/// Read more of the header if we need to
switch header.additionalBytesNeeded {
case 0:
return Observable.just(header)
case let needed:
return self
.stream
.readAndBufferExactly(needed)
.map { WebSocketFrameHeader(original: header, additionalData: $0) }
}
}
}
@warn_unused_result
private func readFrame() -> Observable<WebSocketFrame> {
return readFrameHeader()
.map { header -> WebSocketFrame in
let payload: Observable<UnsafeBufferPointer<UInt8>>
precondition(header.isComplete)
if header.payloadLen == 0 {
payload = Observable.empty()
} else {
if let maskingKey = header.maskingKey {
payload = self.stream.readExactly(header.payloadLen).mask(maskingKey)
} else {
payload = self.stream.readExactly(header.payloadLen)
}
}
return WebSocketFrame(header: header, payload: payload)
}
}
@warn_unused_result
public func readFrames(scheduler: ImmediateSchedulerType) -> Observable<WebSocketFrame> {
return Observable.create { observer in
let disposable = SerialDisposable()
func doNext() -> Disposable {
var seenOne = false
return self
.readFrame()
.subscribe { e in
switch e {
case let .Next(v):
seenOne = true
/// Rewrite the payload to have a side effect of pumping the next frame once it is completely read
let newPayload = v.payload.doOn { e in
switch e {
/// We can start reading the next one once they've finished this one
case .Completed:
disposable.disposable = doNext()
/// If there's an error reading the data, then propagate that to our observer as well
case let .Error(e):
observer.onError(e)
default:
break
}
}
let newFrame = WebSocketFrame(header: v.header, payload: newPayload)
observer.onNext(newFrame)
case .Error:
observer.on(e)
case .Completed:
/// If we didn't see one, we're at the end of the stream
if !seenOne {
observer.onCompleted()
}
}
}
}
disposable.disposable = doNext()
return disposable
}
}
}
extension ObservableType where E == UnsafeBufferPointer<UInt8> {
/// This will mask or unmask the data via xor
/// TODO: make buffer size configurable
/// Not thread safe
func mask(mask: UInt32) -> Observable<UnsafeBufferPointer<UInt8>> {
var mask = mask
return Observable.create { observer in
var writeBuffer = [UInt8]()
var lastOffset = 0
return self
.subscribe { evt in
switch evt {
case let .Next(data):
withUnsafePointer(&mask) { ptr in
let keyBytesStart = unsafeBitCast(ptr, UnsafePointer<UInt8>.self)
let keyLen = sizeof(UInt32)
assert(writeBuffer.isEmpty)
writeBuffer.reserveCapacity(data.count)
// TODO: determine if this slows us down too much
for (i, byte) in data.enumerate() {
writeBuffer.append(byte ^ keyBytesStart.advancedBy((i + lastOffset) % keyLen).memory)
}
lastOffset += data.count
}
writeBuffer.withUnsafeBufferPointer { ptr in
observer.onNext(ptr)
}
writeBuffer.removeAll(keepCapacity: true)
case .Error, .Completed:
observer.on(evt)
}
}
}
}
}
/// Handles the wire format of a websocket. This is pretty much everything past a handshake. It is server/client agnostic
public class WebSocketProtocol {
}
/// Default contains implementation is O(N) for range. This is much faster
extension Range {
public func contains(element: Element) -> Bool {
return startIndex.distanceTo(element) >= 0 && endIndex.distanceTo(element) < 0
}
}

View File

@ -0,0 +1,387 @@
//
// BufferedReading.swift
// SocketRocket
//
// Created by Mike Lewis on 8/11/15.
//
//
import Foundation
import RxSwift
private enum BufferedReadRequestType<C: CollectionType> {
/// If splitfunc finds a match
///
/// - parameter currentBuffer: current buffer to match against
/// - parameter atEnd: if we're at the end of the file. If there's no guaranteed match, the function should throws
typealias SplitFunc = (currentBuffer: C, atEnd: Bool) throws -> (sizeToProduce: C.Index.Distance, finished: Bool)
case Size(sizeRequested: C.Index.Distance)
case OnSplit(splitFunc: SplitFunc)
}
private struct PendingReadRequest<O: ObserverType where O.E: CollectionType> {
typealias C = O.E
typealias RequestType = BufferedReadRequestType<C>
init(type: RequestType, observer: O) {
self.type = type
self.observer = observer
}
/// Contains the request type
let type: RequestType
let observer: O
/// How much we have produced so far
var countProduced: C.Index.Distance = 0
}
/// We will try to read this much when trying to buffer for split requests
private let defaultChunkSizeForSplit = 2048
public protocol SplittableInputStream : StreamBase {
func read(splitFunc splitFunc: (currentBuffer: UnsafeBufferPointer<Element>, atEnd: Bool) throws -> (sizeToProduce: Int, finished: Bool)) -> Observable<UnsafeBufferPointer<Element>>
}
public class BufferedInputStream<Wrapped: InputStream> : InputStream, SplittableInputStream {
private let stream: Wrapped
public typealias Element = Wrapped.Element
private var buffer = Array<Wrapped.Element>()
private typealias PendingRequest = PendingReadRequest<AnyObserver<UnsafeBufferPointer<Wrapped.Element>>>
private var lock = SpinLock()
/// The following must only read or mutated within a lock
private var pendingRequests = [PendingRequest]()
private var readingInProgress = false
/// These are to be called after we're unlocked
private var deferredCalls = Array<() -> ()>()
/// This is set if we get any error back from the stream. Subsequent requests will
private var failError: ErrorType?
/// TODO: make this work
private var atEOF = false
private var isOkToDeferCall = false
/// Disposable for reading
private var currentDisposable: Disposable? = nil
private var outstandingBytes: Int = 0
private var reachedEnd = false
private func locked(@noescape body: () throws -> ()) rethrows {
let deferredCalls: [() -> ()] = try self.lock.locked {
isOkToDeferCall = true
try body()
defer {
isOkToDeferCall = false
self.deferredCalls.removeAll()
}
return self.deferredCalls
}
for c in deferredCalls {
c()
}
}
private func deferCall(call: () -> ()) {
precondition(isOkToDeferCall)
deferredCalls.append(call)
}
init(stream: Wrapped) {
self.stream = stream
}
deinit {
self.currentDisposable?.dispose()
}
private func pump() {
locked {
do {
if readingInProgress {
return
}
if pendingRequests.isEmpty {
return
}
// If we have an error, fail all pending requests
if let error = failError {
let requests = pendingRequests
pendingRequests.removeAll()
deferCall {
for r in requests {
r.observer.onError(error)
}
}
return
}
/// Consume buffer until we can't anymore
repeat {
} while (try tryConsumeBuffer() > 0)
// If we reached the end of the stream then we want to flush all the existing requests
if reachedEnd {
let requests = pendingRequests
pendingRequests.removeAll()
deferCall {
for r in requests {
r.observer.onCompleted()
}
}
return
}
guard let nextRequest = pendingRequests.first else {
return
}
/// Now we take our next request and fetch data for it
let readSize: Int
switch nextRequest.type {
case .OnSplit:
readSize = defaultChunkSizeForSplit
case let .Size(sizeRequested: requested):
/// We should have already consumed this if it were the case
precondition(self.buffer.count < requested)
readSize = requested - self.buffer.count
}
precondition(!readingInProgress)
precondition(currentDisposable == nil)
readingInProgress = true
precondition(outstandingBytes == 0)
outstandingBytes = readSize
deferCall { self.requestData(readSize) }
} catch let e {
/// If we failed, we want to re-run pump
failWhileLocked(e)
deferCall { self.pump() }
}
}
}
public func read(count: Int) -> Observable<UnsafeBufferPointer<Element>> {
return self.read(.Size(sizeRequested: count))
}
public typealias SplitFunc = (currentBuffer: UnsafeBufferPointer<Element>, atEnd: Bool) throws -> (sizeToProduce: Int, finished: Bool)
public func read(splitFunc splitFunc: SplitFunc) -> Observable<UnsafeBufferPointer<Element>> {
return self.read(.OnSplit(splitFunc: splitFunc))
}
private func requestData(count: Int) {
precondition(currentDisposable == nil)
precondition(readingInProgress)
let compositeDisposable = CompositeDisposable()
self.currentDisposable = compositeDisposable
let disposable = self
.stream
.read(count)
.subscribe(self.handle)
compositeDisposable.addDisposable(disposable)
}
private func handle(event: Event<UnsafeBufferPointer<Wrapped.Element>>) {
locked {
switch event {
case .Completed:
currentDisposable?.dispose()
currentDisposable = nil
readingInProgress = false
// If we requested more bytes than we received, we have reached the end of input stream
if outstandingBytes > 0 {
reachedEnd = true
}
case let .Next(value):
outstandingBytes -= value.count
precondition(outstandingBytes >= 0)
do {
try consumeNewData(value)
} catch let e {
failWhileLocked(e)
}
case let .Error(e):
failWhileLocked(e)
}
}
pump()
}
private func failWhileLocked(e: ErrorType) {
readingInProgress = false
self.failError = e
currentDisposable?.dispose()
currentDisposable = nil
}
/// Produces data to the pending requests
private func consumeNewData(newData: UnsafeBufferPointer<Wrapped.Element>) throws {
repeat {
} while (try tryConsumeBuffer() > 0)
// if the buffer is empty we can try to consume the new data. Otherwise, append to buffer
if buffer.isEmpty {
let consumed = try tryConsumeData(newData)
buffer.appendContentsOf(newData[consumed..<newData.endIndex])
} else {
// if the buffer isn't empty, just append the contents
buffer.appendContentsOf(newData)
// Try to consume the buffer again just in case
}
repeat {
} while (try tryConsumeBuffer() > 0)
}
/// Attemps to consume buffered data. Must be called while locked
private func tryConsumeBuffer() throws -> Int {
if buffer.isEmpty {
return 0
}
let consumed = try buffer.withUnsafeBufferPointer(tryConsumeData)
if consumed > 0 {
buffer.removeFirst(consumed)
}
return consumed
}
/// Attempts to consume the data passed in. Must be called while locked
@warn_unused_result
private func tryConsumeData(data: UnsafeBufferPointer<Wrapped.Element>) throws -> Int {
guard var request = self.pendingRequests.first else {
/// if we don't have any pending requests, we didn't consume any
return 0
}
let sizeToProduce: Int
let finished: Bool
switch request.type {
case let .OnSplit(splitFunc: splitFunc):
(sizeToProduce, finished) = try buffer.withUnsafeBufferPointer { try splitFunc(currentBuffer: $0, atEnd: atEOF) }
case let .Size(sizeRequested: requestedSize):
let sizeRemaining = requestedSize - request.countProduced
sizeToProduce = min(data.count, sizeRemaining)
finished = sizeRemaining - sizeToProduce == 0
}
if finished {
self.pendingRequests.removeFirst()
} else {
request.countProduced += sizeToProduce
pendingRequests[0] = request
}
if sizeToProduce > 0 {
precondition(sizeToProduce <= data.count)
let dataToProduce = sizeToProduce == data.count ? data : UnsafeBufferPointer(start: data.baseAddress, count: sizeToProduce)
request.observer.onNext(dataToProduce)
}
if finished {
deferCall {
request.observer.onCompleted()
}
}
return sizeToProduce
}
private func read(requestType: PendingRequest.RequestType) -> Observable<UnsafeBufferPointer<Element>> {
return Observable.create { observer in
let request = PendingRequest(type: requestType, observer: observer)
self.lock.locked {
self.pendingRequests.append(request)
}
self.pump()
return NopDisposable.instance
}
}
}
private let crlf: [UInt8] = [UInt8]("\r\n".utf8)
private let crlfCrlf: [UInt8] = crlf + crlf
let crlfSplitFunc = makeSplitFunc(crlf)
let crlfCrlfSplitFunc = makeSplitFunc(crlfCrlf)
private func makeSplitFunc<E: Equatable
>(separator: [E]) -> (currentBuffer: UnsafeBufferPointer<E>, atEnd: Bool) throws -> (sizeToProduce: Int, finished: Bool) {
let separatorCount = separator.count
return { currentBuffer, atEnd in
var lastPartialMatchCount = 0
continueLabel:
for startPos in currentBuffer.startIndex..<(currentBuffer.startIndex.advancedBy(currentBuffer.count)) {
for checkOffset in 0..<separatorCount {
if startPos.advancedBy(checkOffset) >= currentBuffer.endIndex {
lastPartialMatchCount = checkOffset
break continueLabel
}
if currentBuffer[startPos + checkOffset] != separator[checkOffset] {
continue continueLabel
}
}
// if we got to here, we have a match!
return (startPos + separatorCount, true)
}
/// If we got this far, we can produce all data up to the length
// We need to keep any data that we had a partial match with
return (currentBuffer.count - lastPartialMatchCount, false)
}
}
extension SplittableInputStream where Element == UInt8 {
/// Yields elements until line is read. It may produce multiple chunks
public func readLine() -> Observable<UnsafeBufferPointer<Element>> {
return read(splitFunc: crlfSplitFunc)
}
/// Reads until we get a '\r\n\r\n'. Useful for reading HTTP header
public func readCrlfCrlf() -> Observable<UnsafeBufferPointer<Element>> {
return read(splitFunc: crlfCrlfSplitFunc)
}
}

View File

@ -0,0 +1,68 @@
//
// Codec.swift
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
import RxSwift
enum ValueOrEnd<V> {
/// :param consumed: the size of the input stream consumed
/// :param value: result value for the coded
case Value(V)
/// If we're out of data
case End
}
//
protocol Codec {
typealias InputElement
typealias OutputElement
/// Consumes all the input. Appends
mutating func code<
I: CollectionType,
O: RangeReplaceableCollectionType
where
I.Generator.Element == InputElement,
O.Generator.Element == OutputElement,
I.Index.Distance == Int
>(input: ValueOrEnd<I>, inout output: O) throws
}
extension ObservableType where E: CollectionType, E.Index.Distance == Int {
func encode<C: Codec where C.InputElement == E.Generator.Element>(codecFactory: () -> C) -> Observable<[C.OutputElement]>{
return Observable.create { observer in
var codec = codecFactory()
var outputBuffer = Array<C.OutputElement>()
return self.subscribe { event in
defer { outputBuffer.removeAll(keepCapacity: true) }
do {
switch event {
case .Completed:
try codec.code(ValueOrEnd<Array<C.InputElement>>.End, output: &outputBuffer)
if !outputBuffer.isEmpty {
observer.onNext(outputBuffer)
}
observer.onCompleted()
case let .Error(e):
throw e
case let .Next(value):
try codec.code(.Value(value), output: &outputBuffer)
observer.onNext(outputBuffer)
}
} catch let e {
observer.onError(e)
}
}
}
}
}

View File

@ -0,0 +1,9 @@
//
// Dial.swift
// SocketRocket
//
// Created by Mike Lewis on 1/5/16.
//
//
import Foundation

View File

@ -0,0 +1,83 @@
//
// DispatchIO.swift
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
import Foundation
import RxSwift
/// Wraps an io since it is a protocol
public struct DispatchIO {
public let io: dispatch_io_t
/// queue that results are called on
public let resultQueue: dispatch_queue_t
// We're bound to using a result queue if we want to use dispatch_io underlying
public init(io: dispatch_io_t, resultQueue: dispatch_queue_t) {
self.io = io
self.resultQueue = resultQueue
}
}
extension DispatchIO : StreamBase {
public typealias Element = UInt8
}
extension DispatchIO : InputStream {
public func read(count: Int) -> Observable<UnsafeBufferPointer<UInt8>> {
let io = self.io
return Observable.create { observer in
dispatch_io_read(io, 0, count, self.resultQueue) { done, data, error in
if error != 0 {
observer.onError(POSIXError(rawValue: error)!)
}
let dataSize = dispatch_data_get_size(data)
var buffer = [UInt8]()
buffer.reserveCapacity(dataSize)
data.apply { rawBuffer in
observer.onNext(rawBuffer)
}
if done {
observer.onCompleted()
}
}
return NopDisposable.instance
}
}
}
extension DispatchIO : OutputStream {
public func write(data: UnsafeBufferPointer<UInt8>) -> Observable<Void> {
let doneSubject: ReplaySubject<Void> = ReplaySubject.create(bufferSize: 1)
let dispatchData = dispatch_data_create(data.baseAddress, data.count, nil, nil)
dispatch_io_write(io, 0, dispatchData, resultQueue) { done, _, error in
guard error == 0 else {
doneSubject.onError(POSIXError(rawValue: error)!)
return
}
if done {
doneSubject.onNext()
doneSubject.onCompleted()
}
}
return doneSubject
}
}

View File

@ -0,0 +1,57 @@
//
// Error.swift
// SocketRocket
//
// Created by Mike Lewis on 8/7/15.
//
//
/// Wraps errors. Has an uknown type if it cant resolve to an oserror
enum Error: ErrorType {
case Unknown(status: Int32)
case CodecError
case UTF8DecodeError
case Canceled
case NoAddressesRemaining
case UnknownSockaddrType(family: sa_family_t)
// Getaddrinfo error
case GAIError(status: Int32)
// when inet_pton returns 0
case NotParseableAddress
/// For functions that return negative value on error and expect errno to be set
static func checkReturnCode(returnCode: Int32) -> ErrorType? {
guard returnCode < 0 else {
return nil
}
return errorFromStatusCode(errno)
}
/// Returns an error type based on status code
static func errorFromStatusCode(status: Int32) -> ErrorType? {
guard status != 0 else {
return nil
}
if let e = POSIXError(rawValue: status) {
return e
}
return Error.Unknown(status: status)
}
static func throwIfNotSuccess(status: Int32) throws {
if let e = errorFromStatusCode(status) {
throw e
}
}
// Same as above, but checks if less than 0, and uses errno as the varaible
static func throwIfNotSuccessLessThan0(returnCode: Int32) throws -> Int32 {
if let e = checkReturnCode(returnCode) {
throw e
}
return returnCode
}
}

View File

@ -0,0 +1,53 @@
//
// Extensions.swift
// SocketRocket
//
// Created by Mike Lewis on 8/6/15.
//
//
import Dispatch
extension dispatch_data_t {
func apply(applier: UnsafeBufferPointer<UInt8> -> Bool) -> Bool {
return dispatch_data_apply(self) { (_, offset, buffer, size) -> Bool in
let mappedBuffer = unsafeBitCast(buffer, UnsafePointer<UInt8>.self)
let buffer = UnsafeBufferPointer(start: mappedBuffer.advancedBy(offset), count: size - offset)
return applier(buffer)
}
}
func apply(applier: UnsafeBufferPointer<UInt8> -> ()) -> Bool {
return self.apply { d -> Bool in
applier(d)
return true
}
}
func apply(applier: UnsafeBufferPointer<UInt8> throws -> ()) throws -> Bool {
var error: ErrorType? = nil
let ret = self.apply { d -> Bool in
do {
try applier(d)
return true
} catch let e {
error = e
return false
}
}
if let e = error {
throw e
}
return ret
}
}
extension dispatch_queue_t {
func dispatchAsync(block: () -> ()) {
dispatch_async(self, block)
}
}

26
SocketRocketIO/Info.plist Normal file
View File

@ -0,0 +1,26 @@
<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@ -0,0 +1,64 @@
//
// Locks.swift
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
import Foundation
public protocol Lock {
mutating func lock()
mutating func unlock()
}
public struct SpinLock : Lock {
private var osSpinLock: OSSpinLock = OS_SPINLOCK_INIT
public mutating func lock() {
OSSpinLockLock(&osSpinLock)
}
public mutating func unlock() {
OSSpinLockUnlock(&osSpinLock)
}
}
public class RecursiveLock : Lock {
private var mutex: pthread_mutex_t
public init() {
var attr = pthread_mutexattr_t()
mutex = pthread_mutex_t()
pthread_mutexattr_init(&attr)
defer { pthread_mutexattr_destroy(&attr) }
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)
pthread_mutex_init(&mutex, &attr)
}
deinit {
pthread_mutex_destroy(&mutex)
}
public func lock() {
pthread_mutex_lock(&mutex)
}
public func unlock() {
pthread_mutex_unlock(&mutex)
}
}
extension Lock {
public mutating func locked<Result>(@noescape body: () throws -> Result) rethrows -> Result {
lock()
defer { unlock() }
return try body()
}
}

View File

@ -0,0 +1,181 @@
//
// Loopback.swift
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
import Foundation
import RxSwift
/// Converts an observable of arrays into an InputStream
public struct ObserverInputStream<E> : InputStream {
public typealias Element = E
private let loopback = LoopbackStream<E>()
private let disposeBag = DisposeBag()
private init<OE: CollectionType where OE.Generator.Element == E>(data: Observable<OE>) {
data
.subscribe { evt in
switch evt {
case .Completed:
self.loopback.onCompleted()
case let .Error(e):
self.loopback.onError(e)
case let .Next(buff):
Array(buff).withUnsafeBufferPointer { ptr in
self.loopback.onNext(ptr)
}
}
}
.addDisposableTo(disposeBag)
}
public func read(count: Int) -> Observable<UnsafeBufferPointer<Element>> {
return self.loopback.read(count)
}
}
public extension ObservableType where E: CollectionType {
/// Utility function for helping with tests and the likes
public func asInputStream() -> ObserverInputStream<E.Generator.Element> {
return ObserverInputStream(data: self.asObservable())
}
}
public extension ObservableType where E == String {
/// Utility function for helping with tests and the likes
public func asUTF8InputStream() -> ObserverInputStream<UInt8> {
return ObserverInputStream(data: self.map { Array($0.utf8) } .asObservable())
}
}
extension LoopbackStream : ObserverType {
typealias E = UnsafeBufferPointer<Element>
func on(event: Event<E>) {
switch event {
case .Completed:
self.close()
case let .Next(val):
self.write(val)
case let .Error(e):
self.fail(e)
}
}
}
class LoopbackStream<ElementType> : InputStream, OutputStream {
typealias Element = ElementType
private var dataLock = SpinLock()
/// This is used in pump so we make sure to handle everything in order
private var pumpLock = RecursiveLock()
private var pendingRequests = [(AnyObserver<UnsafeBufferPointer<Element>>, Int, Int)]()
private var buffer = [Element]()
private var closed = false
private var failError: ErrorType?
private var pumpDeferredBlocks = Array<() -> ()>()
private var isPumping = false
func write(data: UnsafeBufferPointer<Element>) -> Observable<Void> {
self.dataLock.locked {
self.buffer.appendContentsOf(data)
}
pump()
return Observable.just()
}
func read(count: Int) -> Observable<UnsafeBufferPointer<Element>> {
return Observable.create { observer in
self.dataLock.locked {
self.pendingRequests.append((observer, count, count))
}
self.pump()
return NopDisposable.instance
}
}
/// To simulate or propagagte errors
func fail(error: ErrorType) {
self.dataLock.locked {
self.closed = true
self.failError = error
}
self.pump()
}
func close() {
self.dataLock.locked {
self.closed = true
}
self.pump()
}
private func pump() {
self.pumpLock.locked {
let isOuterPump = !isPumping
if isOuterPump {
isPumping = true
}
self.dataLock.locked {
while (closed || !buffer.isEmpty) && !pendingRequests.isEmpty {
var request = pendingRequests[0]
let elementsToConsume = min(request.1, buffer.count)
request.1 -= elementsToConsume
let observer = request.0
if elementsToConsume > 0 {
let elements = Array(buffer[0..<elementsToConsume])
self.buffer.removeFirst(elementsToConsume)
self.pumpDeferredBlocks.append {
elements.withUnsafeBufferPointer(observer.onNext)
}
}
if request.1 == 0 || closed {
if let failError = self.failError {
self.pumpDeferredBlocks.append {
observer.onError(failError)
}
} else {
self.pumpDeferredBlocks.append {
observer.onCompleted()
}
}
pendingRequests.removeFirst(1)
} else {
pendingRequests[0] = request
}
}
}
/// If we're the outer pump, handle the deferred blocks
if isOuterPump {
while !self.pumpDeferredBlocks.isEmpty {
let blocks = self.pumpDeferredBlocks
self.pumpDeferredBlocks.removeAll()
for b in blocks {
b()
}
}
isPumping = false
}
}
}
}

View File

@ -0,0 +1,106 @@
//
// Socket.swift
// SwiftledMobile
//
// Created by Michael Lewis on 12/29/15.
// Copyright © 2015 Lolrus Industries. All rights reserved.
//
import Darwin
import RxSwift
//public protocol SockAddr {
// init()
//
// mutating func setup(listenAddr: ListenAddress, listenPort: UInt16) throws
//
// static var size : Int {get}
// static var addressFamily: Int32 { get }
//
// // returns PF_INET6 or PF_INET
// static var protocolFamily: Int32 { get }
//
// func withUnsafeSockaddrPtr<Result>(@noescape body: UnsafePointer<sockaddr> throws -> Result) rethrows -> Result
//}
//
///// Represents various types of addresses that can be listened on
//public enum ListenAddress {
// case Loopback
// case Any
// case IPV6Addr(address: String)
// case IPV4Addr(address: String)
//}
//
//extension sockaddr_in6: SockAddr {
// public mutating func setup(listenAddr: ListenAddress, listenPort: UInt16) throws {
// switch listenAddr {
// case .Any:
// self.sin6_addr = in6addr_any
// case let .IPV6Addr(address: address):
// try Error.throwIfNotSuccess(inet_pton(self.dynamicType.addressFamily, address, &self.sin6_addr))
// case .IPV4Addr:
// fatalError("Cannot listen to IPV4Address in an ipv6 socket")
// case .Loopback:
// self.sin6_addr = in6addr_loopback
// }
//
// self.sin6_port = listenPort.bigEndian
// self.sin6_family = sa_family_t(self.dynamicType.addressFamily)
// self.sin6_len = UInt8(self.dynamicType.size)
// }
//
// public func withUnsafeSockaddrPtr<Result>(@noescape body: UnsafePointer<sockaddr> throws -> Result) rethrows -> Result {
// var copy = self
// return try withUnsafePointer(&copy) { ptr -> Result in
// return try body(unsafeBitCast(ptr, UnsafePointer<sockaddr>.self))
// }
// }
//
// public static let size = sizeof(sockaddr_in6)
// public static let addressFamily = AF_INET6
// public static let protocolFamily = PF_INET6
//}
//
//let INADDR_ANY = in_addr(s_addr: 0x00000000)
//let INADDR_LOOPBACK4 = in_addr(s_addr: UInt32(0x7f000001).bigEndian)
//
//extension sockaddr_in: SockAddr {
// public static let size = sizeof(sockaddr_in)
// public static let addressFamily = AF_INET
// public static let protocolFamily = PF_INET
//
// public mutating func setup(listenAddr: ListenAddress, listenPort: UInt16) throws {
// switch listenAddr {
// case .Any:
// self.sin_addr = INADDR_ANY
// case let .IPV4Addr(address: address):
// try Error.throwIfNotSuccess(inet_pton(self.dynamicType.addressFamily, address, &self.sin_addr))
// case .IPV6Addr:
// fatalError("Cannot listen to IPV6Address in an ipv4 socket")
// case .Loopback:
// self.sin_addr = INADDR_LOOPBACK4
// }
//
// self.sin_port = listenPort.bigEndian
// self.sin_family = sa_family_t(self.dynamicType.addressFamily)
// self.sin_len = UInt8(self.dynamicType.size)
// }
//
// public func withUnsafeSockaddrPtr<Result>(@noescape body: UnsafePointer<sockaddr> throws -> Result) rethrows -> Result {
// var copy = self
// return try withUnsafePointer(&copy) { ptr -> Result in
// return try body(unsafeBitCast(ptr, UnsafePointer<sockaddr>.self))
// }
// }
//}
//
//
//public extension SockAddr {
// var addr: Darwin.sockaddr {
// return self.withUnsafeSockaddrPtr { ptr in
// return ptr.memory
// }
// }
//}

568
SocketRocketIO/Socket.swift Normal file
View File

@ -0,0 +1,568 @@
////
//// Socket.swift
//// SocketRocket
////
//// Created by Mike Lewis on 8/11/15.
////
////
//
///// Wrappers around BSD sockets and file descriptors and other things
//
//import Swift
//import SystemShims
//
//public struct CloseFlags : OptionSetType {
// public init(rawValue: UInt) {
// self.rawValue = rawValue
// }
// public let rawValue: UInt
// public static let Stop = CloseFlags(rawValue: DISPATCH_IO_STOP)
//}
//
//public protocol SockAddr {
// init()
//
// var family: sa_family_t { get set }
// var len: UInt8 { get set }
// var port: UInt16 { get set }
//
//
// mutating func setAddress(address: Address) throws
//
// /// This is the default address family
// static var addressFamily: sa_family_t { get }
// /// Size to set socklen to
// static var size: Int { get }
//
//
// mutating func withUnsafeSockAddrMutablePointer<T>(@noescape fn: (ptr: UnsafeMutablePointer<sockaddr>, len: socklen_t) -> (T)) -> T
// mutating func withUnsafeSockAddrPointer<T>(@noescape fn: (ptr: UnsafePointer<sockaddr>, len: socklen_t) -> (T)) -> T
//}
//
//
////public enum Address {
//// case Loopback
//// case Any
//// case IPv6Addr(address: String)
//// case IPv4Addr(address: String)
////}
//
//extension sockaddr_in6: SockAddr {
// public var family: sa_family_t {
// get {
// return self.sin6_family
// }
// set {
// self.sin6_family = newValue
// }
// }
//
// public var port: UInt16 {
// get {
// return self.sin6_port.bigEndian
// }
// set {
// self.sin6_port = newValue.bigEndian
// }
// }
//
// public var len: UInt8 {
// get {
// return self.sin6_len
// }
// set {
// self.sin6_len = newValue
// }
// }
//
// mutating public func setAddress(address: Address) throws {
// switch address {
// case .Any:
// self.sin6_addr = in6addr_any
// case let .IPv6Addr(address: address):
// switch inet_pton(Int32(sockaddr_in6.addressFamily), address, &self.sin6_addr) {
// case 0:
// throw Error.NotParseableAddress
// case -1:
// throw POSIXError(rawValue: errno)!
// default:
// break
// }
// case .IPv4Addr:
// fatalError("Cannot listen to IPV4Address in an ipv6 socket")
// case .Loopback:
// self.sin6_addr = in6addr_loopback
// }
// }
//
// mutating public func withUnsafeSockAddrMutablePointer<T>(@noescape fn: (ptr: UnsafeMutablePointer<sockaddr>, len: socklen_t) -> (T)) -> T {
// return withUnsafeMutablePointer(&self) { ptr in
// return fn(ptr: unsafeBitCast(ptr, UnsafeMutablePointer<sockaddr>.self), len: socklen_t(sizeofValue(self)))
// }
// }
//
// mutating public func withUnsafeSockAddrPointer<T>(@noescape fn: (ptr: UnsafePointer<sockaddr>, len: socklen_t) -> (T)) -> T {
// return withUnsafePointer(&self) { ptr in
// return fn(ptr: unsafeBitCast(ptr, UnsafePointer<sockaddr>.self), len: socklen_t(sizeofValue(self)))
// }
// }
//
// public static let size = sizeof(sockaddr_in6)
// public static let addressFamily = sa_family_t(AF_INET6)
//}
//
//let INADDR_ANY = in_addr(s_addr: 0x00000000)
//let INADDR_LOOPBACK4 = in_addr(s_addr: UInt32(0x7f000001).bigEndian)
//
//extension sockaddr_in: SockAddr {
// public static let size = sizeof(sockaddr_in)
// public static let addressFamily = sa_family_t(AF_INET)
//
// public var family: sa_family_t {
// get {
// return self.sin_family
// }
// set {
// self.sin_family = newValue
// }
// }
//
// public var port: UInt16 {
// get {
// return self.sin_port.bigEndian
// }
// set {
// self.sin_port = newValue.bigEndian
// }
// }
//
// public var len: UInt8 {
// get {
// return self.sin_len
// }
// set {
// self.sin_len = newValue
// }
// }
//
// mutating public func setAddress(address: Address) throws {
// switch address {
// case .Any:
// self.sin_addr = INADDR_ANY
// case let .IPv4Addr(address: address):
// switch inet_pton(Int32(sockaddr_in.addressFamily), address, &self.sin_addr) {
// case 0:
// throw Error.NotParseableAddress
// case -1:
// throw POSIXError(rawValue: errno)!
// default:
// break
// }
// case .IPv6Addr:
// fatalError("Cannot listen to IPV6Address in an ipv4 socket")
// case .Loopback:
// self.sin_addr = INADDR_LOOPBACK4
// }
// }
//
//
// mutating public func withUnsafeSockAddrMutablePointer<T>(@noescape fn: (ptr: UnsafeMutablePointer<sockaddr>, len: socklen_t) -> (T)) -> T {
// return withUnsafeMutablePointer(&self) { ptr in
// return fn(ptr: unsafeBitCast(ptr, UnsafeMutablePointer<sockaddr>.self), len: socklen_t(sizeofValue(self)))
// }
// }
//
// mutating public func withUnsafeSockAddrPointer<T>(@noescape fn: (ptr: UnsafePointer<sockaddr>, len: socklen_t) -> (T)) -> T {
// return withUnsafePointer(&self) { ptr in
// return fn(ptr: unsafeBitCast(ptr, UnsafePointer<sockaddr>.self), len: socklen_t(sizeofValue(self)))
// }
// }
//}
//
//
//
//public protocol FileLike {
// var fd: dispatch_fd_t { get }
//}
//
//extension FileLike {
// /// Calls fcntl(fd, F_GETFL)
// func fcntlGetFlags() throws -> Int32 {
// return try Error.throwIfNotSuccessLessThan0(shim_fcntl(fd, F_GETFL, 0))
// }
//
// /// Calls fcntl(fd, F_SETFL, flags)
// func fcntlSetFlags(flags: Int32) throws {
// try Error.throwIfNotSuccessLessThan0(shim_fcntl(fd, F_SETFL, flags))
// }
//
// func close() throws {
// try Error.throwIfNotSuccessLessThan0(Darwin.close(fd))
// }
//}
//
//extension sockaddr_union {
// /// Helper function that simulates creating a union and then returns the apppriate sockaddr
// /// :param: block Takes unsafe pointer of a sockaddr with the len socklen_t.
// /// inside the block, one should call something like Darwin.accept or Darwin.sockname
// public static func withUnsafeMutableSockaddrInput(@noescape block: (ptr: UnsafeMutablePointer<sockaddr>, inout len: socklen_t) -> (Int32)) throws -> (result: Int32, addr: SockAddr) {
// // We're going to make an ipv6 one since it is longer
// var addr = sockaddr_union()
// var socklen = socklen_t(sizeof(sockaddr_union.self))
//
// let (result, addrbase): (Int32, sockaddr) = withUnsafeMutablePointer(&addr) { ptr in
// let saptr = sockaddr_union_getsockaddr(ptr)
//
// let s = block(ptr: saptr, len: &socklen)
//
// return (s, saptr.memory)
// }
//
// switch addrbase.sa_family {
// case sa_family_t(AF_INET):
// precondition(Int(addrbase.sa_len) == sizeof(sockaddr_in.self))
// precondition(Int(socklen) == sizeof(sockaddr_in.self))
// return (result, sockaddr_union_getsockaddr_in(&addr).memory)
// case sa_family_t(AF_INET6):
// return (result, sockaddr_union_getsockaddr_in6(&addr).memory)
// default:
// throw Error.UnknownSockaddrType(family: addrbase.sa_family)
// }
//
// }
//}
//
//
//public struct Socket: FileLike {
// public let fd: dispatch_fd_t
//
// init(fd: dispatch_fd_t) {
// self.fd = fd
// }
//
// init(addressInfoFamily: Int32, socktype: Int32 = SOCK_STREAM, proto: Int32 = IPPROTO_TCP) throws {
// fd = try Error.throwIfNotSuccessLessThan0(socket(addressInfoFamily, socktype, proto))
// }
//
// /// Wrapper around accept
// func accept() throws -> (socket: Socket, addr: SockAddr) {
// let r = try sockaddr_union.withUnsafeMutableSockaddrInput { (ptr: UnsafeMutablePointer<sockaddr>, inout len: socklen_t) -> (Int32) in
// return Darwin.accept(self.fd, ptr, &len)
// }
//
// return (Socket(fd: r.result), r.addr)
// }
//
// func bind(var addr: SockAddr) throws {
// try Error.throwIfNotSuccessLessThan0(addr.withUnsafeSockAddrMutablePointer({ (ptr, len) in
// return Darwin.bind(fd, ptr, len)
// }))
// }
//
// func connect(var addr: SockAddr) throws {
// try Error.throwIfNotSuccessLessThan0(addr.withUnsafeSockAddrMutablePointer({ (ptr, len) in
// return Darwin.connect(fd, ptr, len)
// }))
// }
//
// static let DefaultListenBacklog: Int32 = 5
//
// func listen(backlog: Int32 = DefaultListenBacklog) throws {
// try Error.throwIfNotSuccessLessThan0(Darwin.listen(fd, backlog))
// }
//
// func sockname() throws -> SockAddr {
// return try sockaddr_union.withUnsafeMutableSockaddrInput { (ptr: UnsafeMutablePointer<sockaddr>, inout len: socklen_t) -> (Int32) in
// return Darwin.getsockname(self.fd, ptr, &len)
// }.addr
// }
//
// /// :param: option option like SO_REUSEPORT or SO_REUSEADDR
// func setsockopt(option: Int32, var value: Int32) throws {
// try Error.throwIfNotSuccessLessThan0(Darwin.setsockopt(fd, SOL_SOCKET, option, &value, socklen_t(sizeofValue(value))))
// }
//
// func getsockopt(option: Int32) throws -> Int32 {
// var value: Int32 = 0
// var len = socklen_t(sizeofValue(value))
// try Error.throwIfNotSuccessLessThan0(Darwin.getsockopt(fd, SOL_SOCKET, option, &value, &len))
// return value
// }
//}
//
//extension Socket {
// /// Returns a socket that is bound to address and listening.
// /// The socket is set to have REUSEADDR and REUSEPORT and O_NONBLOCK
// static func boundListeningSocket(socktype: SockAddr.Type, address: Address, port: UInt16 = 0) throws -> Socket {
// var successful = false
//
// let s = try Socket(addressInfoFamily: Int32(socktype.addressFamily))
//
// defer {
// if !successful {
// do {
// try s.close()
// } catch {
// // Do nothing. we're already probably throwing an exceptions
// }
// }
// }
//
// try s.fcntlSetFlags(s.fcntlGetFlags() | O_NONBLOCK)
//
// try s.setsockopt(SO_REUSEADDR, value: 1)
// try s.setsockopt(SO_REUSEPORT, value: 1)
//
// var addr = socktype.init()
//
// addr.port = port
// addr.len = UInt8(socktype.size)
// addr.family = socktype.addressFamily
//
// try addr.setAddress(address)
//
// try s.bind(addr)
// try s.listen()
//
// successful = true
//
// return s
// }
//
// /// Starts accepting connections on workQueue. Generally called after boundListeningSocket
// public func startAccepting(workQueue: dispatch_queue_t, acceptHandler:(socket: Socket) -> Void) -> (cancelResolver: Resolver<Void>, closedPromise: Promise<Void>) {
// precondition(fd >= 0)
//
// let (cancelResolver, canceledPromise) = VoidPromiseType.resolver(workQueue)
// let (closedResolver, closedPromise) = VoidPromiseType.resolver(workQueue)
//
// let eventSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, UInt(fd), 0, workQueue.queue)
//
// canceledPromise.then { _ in
// dispatch_source_cancel(eventSource)
// }
//
// dispatch_source_set_event_handler(eventSource) {
// do {
// acceptHandler(socket: try self.accept().socket)
// } catch {
// /// Ignore errors for now. Should we cancel the stream?
// }
// }
//
// dispatch_source_set_cancel_handler(eventSource) {
// precondition(self.fd >= 0)
// do {
// try self.close()
// } catch {
// /// Ignore
// }
//
// closedResolver.resolve()
// }
//
// dispatch_resume(eventSource);
//
// return (cancelResolver, closedPromise)
// }
//
//}
//
//extension dispatch_data_t {
// var empty: Bool {
// get {
// return dispatch_data_empty === self
// }
// }
//}
//
//private var hints: addrinfo = {
// var hints = addrinfo()
// hints.ai_family = PF_UNSPEC
// hints.ai_socktype = SOCK_STREAM
// return hints
// }()
//
//
//extension dispatch_queue_t {
// /// Used to dispatch synchronous operations on a specific queue
// /// :param queue: queue that promise is constructed with. If nil, will use self.
// func blockingPromise<T>(queue: dispatch_queue_t? = nil, blockingFn: () throws -> T) -> Promise<T> {
// let (r, p) = Promise<T>.resolver(queue ?? self)
//
// self.dispatchAsync {
// r.attemptResolve(blockingFn)
// }
//
// return p
// }
//}
//
//
//extension Socket {
// public static func tryConnect(queue: dispatch_queue_t, sockAddr: SockAddr) -> Promise<Socket> {
// let (r, p) = Promise<Socket>.resolver(queue)
//
// let socket: Socket
// do {
// socket = try Socket(addressInfoFamily: Int32(sockAddr.family))
// } catch let e {
// r.reject(e)
// return p
// }
//
// let writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, UInt(socket.fd), UInt(0), queue.queue)
//
// dispatch_source_set_event_handler(writeSource) {
// do {
// let sockerr = try socket.getsockopt(SO_ERROR)
// try Error.throwIfNotSuccessLessThan0(sockerr)
//
// // If we get this far, we're connected!
//
// dispatch_suspend(writeSource)
//
// /// Give it a null handler since we want to keep our FD
// dispatch_source_set_cancel_handler(writeSource) {
// r.resolve(socket)
// }
//
// dispatch_resume(writeSource)
//
// dispatch_source_cancel(writeSource)
//
// } catch POSIXError.EINPROGRESS {
// return
// } catch let e {
// dispatch_source_cancel(writeSource)
// r.reject(e)
// }
// }
//
// dispatch_source_set_cancel_handler(writeSource) {
// do {
// try socket.close()
// } catch { }
// }
//
// dispatch_resume(writeSource)
//
// do {
// try socket.connect(sockAddr)
// } catch POSIXError.EINPROGRESS {
// } catch let e {
// dispatch_source_cancel(writeSource)
// r.reject(e)
// return p
// }
//
// return p
// }
//
// // Tries to connect to a list of addresses
// // Returns a future with the file descriptor that it connected to
// public static func tryConnect(queue: dispatch_queue_t, sockAddrs: [SockAddr]) -> Promise<Socket> {
// guard sockAddrs.count > 0 else {
// return Promise<Socket>.reject(queue, error: Error.NoAddressesRemaining)
// }
//
// func runAtIdx(idx: Int) -> Promise<Socket> {
// return tryConnect(queue, sockAddr: sockAddrs[idx])
// .then { v -> PromiseOrValue<Socket> in
// switch v {
// /// If we're the last one in the array propagate the last error
// case let .Error(e) where idx == sockAddrs.count - 1:
// return .Value(.Error(e))
// case .Error:
// return .Promised(runAtIdx(idx + 1))
// case .Some:
// return .Value(v)
// }
// }
// }
//
// return runAtIdx(0)
// }
//
// public static func tryConnect(queue: dispatch_queue_t, hostname: String, port: UInt16) -> Promise<Socket> {
// return AddrInfo.getAddrInfo(queue, hostname: hostname, servname: "\(port)")
// .thenSplit { v -> PromiseOrValue<Socket> in
// let sockaddrs = v.map { ai in return ai.sockAddr}
// return .Promised(Socket.tryConnect(queue, sockAddrs: sockaddrs))
// }
// }
//}
///// denormalized addrinfo. This way we can pass it w/o holding onto memory
//public struct AddrInfo {
// /// AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST, etc.
// var flags: Int32
// /// PF_*
// var family: Int32
// /// SOCK_*
// var socktype: Int32
// /// 0 or IPPROTO_xxx for IPv4 and IPv6
// var proto: Int32
// var canonname: String?
// let sockAddr: SockAddr
//
//
// init?(ai: addrinfo) {
// flags = ai.ai_flags
// family = ai.ai_family
// socktype = ai.ai_socktype
// proto = ai.ai_protocol
// canonname = String.fromCString(ai.ai_canonname)
//
// switch family {
// case AF_INET:
// precondition(ai.ai_addrlen == socklen_t(sizeof(sockaddr_in.self)))
// sockAddr = unsafeBitCast(ai.ai_addr, UnsafePointer<sockaddr_in>.self).memory
// case AF_INET6:
// precondition(ai.ai_addrlen == socklen_t(sizeof(sockaddr_in6.self)))
// sockAddr = unsafeBitCast(ai.ai_addr, UnsafePointer<sockaddr_in6>.self).memory
// default:
// return nil
// }
// }
//
// // Blocking version getaddrinfo
// private static func getAddrInfo(hostname: String, servname: String) throws -> [AddrInfo] {
// var ai: UnsafeMutablePointer<addrinfo> = nil
// defer {
// if ai != nil {
// freeaddrinfo(ai)
// }
// }
//
// let r = getaddrinfo(hostname, servname, &hints, &ai)
// guard r == 0 else {
// throw Error.GAIError(status: r)
// }
//
// var ret = [AddrInfo]()
//
// for var curAi = ai; curAi != nil; curAi = curAi.memory.ai_next {
// if let ai = AddrInfo(ai: curAi.memory) {
// ret.append(ai)
// }
// }
//
// return ret;
// }
//
// /// async version of getaddrinfo
// ///
// /// - parameter queue: dispatch_queue_t which the resulting promise is created on
// /// - parameter workQueue: dispatch_queue_t that the blocking part of getAddrInfo is run on. This should be a non-serial queue
// /// - parameter hostname: hostname to resolve
// /// - parameter servname: usually stringified port. see `man getaddrinfo` for more details
// public static func getAddrInfo(queue: dispatch_queue_t, workQueue: dispatch_queue_t = dispatch_queue_t.defaultGlobalQueue, hostname: String, servname: String) -> Promise<[AddrInfo]> {
// let (r, p) = Promise<[AddrInfo]>.resolver(queue)
// workQueue.dispatchAsync {
// r.attemptResolve {
// return try getAddrInfo(hostname, servname: servname)
// }
// }
// return p
// }
//}

View File

@ -0,0 +1,18 @@
//
// SocketRocketIO.h
// SocketRocketIO
//
// Created by Mike Lewis on 7/30/15.
//
//
#import <Foundation/Foundation.h>
//! Project version number for SocketRocketIO.
FOUNDATION_EXPORT double SocketRocketIOVersionNumber;
//! Project version string for SocketRocketIO.
FOUNDATION_EXPORT const unsigned char SocketRocketIOVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <SocketRocketIO/PublicHeader.h>

127
SocketRocketIO/Stream.swift Normal file
View File

@ -0,0 +1,127 @@
//
// Stream.swift
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
import Foundation
import RxSwift
public protocol StreamBase {
typealias Element
}
public protocol OutputStream : StreamBase {
/// Writes data to the the stream. Data is not valid after call
/// - returns: Hot observable yields one value when its done then immediately closes.
func write(data: UnsafeBufferPointer<Element>) -> Observable<Void>
}
extension OutputStream {
func write(data: Array<Element>) -> Observable<Void> {
return data.withUnsafeBufferPointer { ptr in
return self.write(ptr)
}
}
}
public struct AnyInputStream<T> : InputStream {
public typealias Element = T
private let readFunc: (count: Int) -> Observable<UnsafeBufferPointer<T>>
private init<S: InputStream where S.Element == T>(stream: S) {
self.readFunc = stream.read
}
public func read(count: Int) -> Observable<UnsafeBufferPointer<T>> {
return self.readFunc(count: count)
}
}
public func anyInputStream<S: InputStream>(stream: S) -> AnyInputStream<S.Element> {
return .init(stream: stream)
}
public protocol InputStream : StreamBase {
/// Reads data until it reaches the number of bytes or end of stream. EOF is not interpreted as an error. Passing in Int.max is valid
/// - returns: Cold readable that enqueues read operation once subscribed. If subscribed to more than once, it will read data more than once
@warn_unused_result
func read(count: Int) -> Observable<UnsafeBufferPointer<Element>>
}
public enum StreamError : Int, ErrorType {
case InvalidLength = 1
}
public extension InputStream {
/// Same as read, but will error if we don't have enough bytes for expected length
func readExactly(count: Int) -> Observable<UnsafeBufferPointer<Element>> {
return Observable.create { observer in
var seen = 0
return self
.read(count)
.subscribe { event in
switch event {
case let .Next(val):
seen += val.count
case .Completed:
if seen != count {
observer.onError(StreamError.InvalidLength)
return
}
default: break
}
observer.on(event)
}
}
}
/// Combines everything into one buffer and delivers it at the end.
/// It has a short circuit case if the first received value is exactly the expected length. This avoids buffering and copying of data.
/// If allowEmpty is true, it will yield an empty array if we dont' see anything. THis is Good for EOF stuff
func readAndBufferExactly(count: Int, allowEmpty: Bool = false) -> Observable<UnsafeBufferPointer<Self.Element>> {
return Observable.create { observer in
/// Buffer if we need one
var buffer = [Element]()
var seen = 0
return self
.read(count)
.subscribe { event in
switch event {
case let .Next(val):
/// Special case for reading the exact ammount to start. This avoids copying data
if seen == 0 && val.count == count {
seen += val.count
observer.onNext(val)
} else {
seen += val.count
buffer.appendContentsOf(val)
}
case .Completed:
guard seen == count || (allowEmpty && seen == 0) else {
observer.onError(StreamError.InvalidLength)
return
}
if !buffer.isEmpty {
buffer.withUnsafeBufferPointer { ptr in
observer.onNext(ptr)
}
}
observer.onCompleted()
case let .Error(e):
observer.onError(e)
}
}
}
}
}

View File

@ -0,0 +1,104 @@
//
// Unicode.swift
// SocketRocket
//
// Created by Mike Lewis on 8/6/15.
//
//
extension UTF8 {
/// Returns number of code units. Throws if its not the first bite of a unicode charactser
/// result includes selve
static func numCodeUnits(first: CodeUnit) throws -> Int {
guard first & 0b1100_0000 != 0b1000_0000 else {
throw Error.UTF8DecodeError
}
// If the first bit is 0, its a single code-point
if first & 0b1000_0000 == 0b0000_0000 {
return 1
}
if first & 0b1110_0000 == 0b1100_0000 {
return 2
}
if first & 0b1111_0000 == 0b1110_0000 {
return 3
}
if first & 0b1111_1000 == 0b1111_0000 {
return 4
}
throw Error.UTF8DecodeError
}
/// Returns the number of valid codeunits from the generator
static func numValidCodeUnits<G: GeneratorType where G.Element == CodeUnit>(var g: G) throws -> Int {
var numValidCodeUnits = 0
outOfCharacters:
for var c = g.next(); c != nil; c = g.next() {
let numCodeUnits = try UTF8.numCodeUnits(c!)
for _ in 0..<(numCodeUnits - 1) {
if let c = g.next() {
guard UTF8.isContinuation(c) else {
throw Error.UTF8DecodeError
}
} else {
break outOfCharacters
}
}
numValidCodeUnits += numCodeUnits
}
return numValidCodeUnits
}
}
struct RawUTF8Codec : Codec {
typealias InputElement = CodeUnit
typealias OutputElement = UnicodeScalar
typealias CodeUnit = UInt8
typealias UnicodeCodec = UTF8
/// Used to buffer unfinished UTF8 Sequences
var inputBuffer = [CodeUnit]()
/// Consumes to our outputbuffer
mutating func consume<I : GeneratorType, O : RangeReplaceableCollectionType where I.Element == InputElement, O.Generator.Element == OutputElement>(var g: I, inout output: O) throws {
var uc = UTF8()
while true {
switch uc.decode(&g) {
case .EmptyInput:
return
case .Error:
throw Error.UTF8DecodeError
case let .Result(scalar):
output.append(scalar)
}
}
}
mutating func code<I : CollectionType, O : RangeReplaceableCollectionType where I.Generator.Element == InputElement, O.Generator.Element == OutputElement, I.Index.Distance == Int>(input: ValueOrEnd<I>, inout output: O) throws {
switch input {
case .End:
if !inputBuffer.isEmpty {
throw Error.UTF8DecodeError
}
case let .Value(v):
inputBuffer.appendContentsOf(v)
let numValidCodeUnits = try UTF8.numValidCodeUnits(inputBuffer.generate())
let slice = inputBuffer[0..<numValidCodeUnits]
try consume(slice.generate(), output: &output)
inputBuffer.removeFirst(numValidCodeUnits)
}
}
}

View File

@ -0,0 +1,131 @@
//
// BufferedInputStreamTests.swift
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
import Foundation
import XCTest
@testable import SocketRocketIO
class BufferedInputStreamTests: XCTestCase {
func testReadSize() {
let loopback = LoopbackStream<UInt8>()
let bufferedInput = BufferedInputStream(stream: loopback)
loopback.write(Array("abcdefg".utf8))
let val = bufferedInput
.read(3)
.map(Array.init)
.subscribeFuture(self)
.get()
.flatMap { $0 }
XCTAssertEqual(val, Array("abc".utf8))
let future2 = bufferedInput
.read(10)
.map(Array.init)
.subscribeFuture(self)
loopback.write(Array("hij".utf8))
loopback.close()
let val2 = future2.get()
.flatMap { $0 }
XCTAssertEqual(val2, Array("defghij".utf8))
}
func testReadUTF8() {
let loopback = LoopbackStream<UInt8>()
let bufferedInput = BufferedInputStream(stream: loopback)
loopback.write(Array("abcdefg".utf8))
let val = bufferedInput
.read(3)
.encode(RawUTF8Codec.init)
.subscribeFuture(self)
.get()
.flatMap { $0 }
var str = ""
str.unicodeScalars.appendContentsOf(val)
XCTAssertEqual(str, "abc")
}
func testSplitterCRLF() {
var buf = [UInt8]("omg\r\na b c d e\r\n".utf8)
var (len, finished) = buf.withUnsafeBufferPointer { try! crlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 5)
XCTAssertTrue(finished)
buf = [UInt8]("omgomg\r".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 6)
XCTAssertFalse(finished)
buf = [UInt8]("\r".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 0)
XCTAssertFalse(finished)
}
func testSplitterCRLFcRLF() {
var buf = [UInt8]("omg\r\na b c d e\r\n\r\n".utf8)
var (len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, buf.count)
XCTAssertTrue(finished)
buf = [UInt8]("\r\n\r\n".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, buf.count)
XCTAssertTrue(finished)
buf = [UInt8]("omgomg\r".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 6)
XCTAssertFalse(finished)
buf = [UInt8]("\r".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 0)
XCTAssertFalse(finished)
buf = [UInt8]("aaa".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 3)
XCTAssertFalse(finished)
buf = [UInt8]("aaaa".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 4)
XCTAssertFalse(finished)
buf = [UInt8]("aaaa\r".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 4)
XCTAssertFalse(finished)
buf = [UInt8]("aaaa\r\n".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 4)
XCTAssertFalse(finished)
buf = [UInt8]("aaaa\r\n\r".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 4)
XCTAssertFalse(finished)
buf = [UInt8]("".utf8)
(len, finished) = buf.withUnsafeBufferPointer { try! crlfCrlfSplitFunc(currentBuffer: $0, atEnd: true) }
XCTAssertEqual(len, 0)
XCTAssertFalse(finished)
}
}

View File

@ -0,0 +1,5 @@
hello
this
is a file
I like to read from it

View File

@ -0,0 +1,212 @@
UTF-8 encoded sample plain-text file
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
Markus Kuhn [ˈmaʳkʊs kuːn] <http://www.cl.cam.ac.uk/~mgk25/> — 2002-07-25
The ASCII compatible UTF-8 encoding used in this plain-text file
is defined in Unicode, ISO 10646-1, and RFC 2279.
Using Unicode/UTF-8, you can write in emails and source code things such as
Mathematics and sciences:
∮ E⋅da = Q, n → ∞, ∑ f(i) = ∏ g(i), ⎧⎡⎛┌─────┐⎞⎤⎫
⎪⎢⎜│a²+b³ ⎟⎥⎪
∀x∈: ⌈x⌉ = x⌋, α ∧ ¬β = ¬(¬α β), ⎪⎢⎜│───── ⎟⎥⎪
⎪⎢⎜⎷ c₈ ⎟⎥⎪
⊆ ℕ₀ ⊂ , ⎨⎢⎜ ⎟⎥⎬
⎪⎢⎜ ∞ ⎟⎥⎪
⊥ < a ≠ b ≡ c ≤ d ≪ ⇒ (⟦A⟧ ⇔ ⟪B⟫), ⎪⎢⎜ ⎲ ⎟⎥⎪
⎪⎢⎜ ⎳aⁱ-bⁱ⎟⎥⎪
2H₂ + O₂ ⇌ 2H₂O, R = 4.7 kΩ, ⌀ 200 mm ⎩⎣⎝i=1 ⎠⎦⎭
Linguistics and dictionaries:
ði ıntəˈnæʃənəl fəˈnɛtık əsoʊsiˈeıʃn
Y [ˈʏpsilɔn], Yen [jɛn], Yoga [ˈjoːgɑ]
APL:
((VV)=V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
Nicer typography in plain text files:
╔══════════════════════════════════════════╗
║ ║
║ • single and “double” quotes ║
║ ║
║ • Curly apostrophes: “Weve been here” ║
║ ║
║ • Latin-1 apostrophe and accents: '´` ║
║ ║
║ • deutsche „Anführungszeichen“ ║
║ ║
║ • †, ‡, ‰, •, 34, —, 5/+5, ™, … ║
║ ║
║ • ASCII safety test: 1lI|, 0OD, 8B ║
║ ╭─────────╮ ║
║ • the euro symbol: │ 14.95 € │ ║
║ ╰─────────╯ ║
╚══════════════════════════════════════════╝
Combining characters:
STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑
Greek (in Polytonic):
The Greek anthem:
Σὲ γνωρίζω ἀπὸ τὴν κόψη
τοῦ σπαθιοῦ τὴν τρομερή,
σὲ γνωρίζω ἀπὸ τὴν ὄψη
ποὺ μὲ βία μετράει τὴ γῆ.
᾿Απ᾿ τὰ κόκκαλα βγαλμένη
τῶν ῾Ελλήνων τὰ ἱερά
καὶ σὰν πρῶτα ἀνδρειωμένη
χαῖρε, ὦ χαῖρε, ᾿Ελευθεριά!
From a speech of Demosthenes in the 4th century BC:
Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν, ὦ ἄνδρες ᾿Αθηναῖοι,
ὅταν τ᾿ εἰς τὰ πράγματα ἀποβλέψω καὶ ὅταν πρὸς τοὺς
λόγους οὓς ἀκούω· τοὺς μὲν γὰρ λόγους περὶ τοῦ
τιμωρήσασθαι Φίλιππον ὁρῶ γιγνομένους, τὰ δὲ πράγματ᾿
εἰς τοῦτο προήκοντα, ὥσθ᾿ ὅπως μὴ πεισόμεθ᾿ αὐτοὶ
πρότερον κακῶς σκέψασθαι δέον. οὐδέν οὖν ἄλλο μοι δοκοῦσιν
οἱ τὰ τοιαῦτα λέγοντες ἢ τὴν ὑπόθεσιν, περὶ ἧς βουλεύεσθαι,
οὐχὶ τὴν οὖσαν παριστάντες ὑμῖν ἁμαρτάνειν. ἐγὼ δέ, ὅτι μέν
ποτ᾿ ἐξῆν τῇ πόλει καὶ τὰ αὑτῆς ἔχειν ἀσφαλῶς καὶ Φίλιππον
τιμωρήσασθαι, καὶ μάλ᾿ ἀκριβῶς οἶδα· ἐπ᾿ ἐμοῦ γάρ, οὐ πάλαι
γέγονεν ταῦτ᾿ ἀμφότερα· νῦν μέντοι πέπεισμαι τοῦθ᾿ ἱκανὸν
προλαβεῖν ἡμῖν εἶναι τὴν πρώτην, ὅπως τοὺς συμμάχους
σώσομεν. ἐὰν γὰρ τοῦτο βεβαίως ὑπάρξῃ, τότε καὶ περὶ τοῦ
τίνα τιμωρήσεταί τις καὶ ὃν τρόπον ἐξέσται σκοπεῖν· πρὶν δὲ
τὴν ἀρχὴν ὀρθῶς ὑποθέσθαι, μάταιον ἡγοῦμαι περὶ τῆς
τελευτῆς ὁντινοῦν ποιεῖσθαι λόγον.
Δημοσθένους, Γ´ ᾿Ολυνθιακὸς
Georgian:
From a Unicode conference invitation:
გთხოვთ ახლავე გაიაროთ რეგისტრაცია Unicode-ის მეათე საერთაშორისო
კონფერენციაზე დასასწრებად, რომელიც გაიმართება 10-12 მარტს,
ქ. მაინცში, გერმანიაში. კონფერენცია შეჰკრებს ერთად მსოფლიოს
ექსპერტებს ისეთ დარგებში როგორიცაა ინტერნეტი და Unicode-ი,
ინტერნაციონალიზაცია და ლოკალიზაცია, Unicode-ის გამოყენება
ოპერაციულ სისტემებსა, და გამოყენებით პროგრამებში, შრიფტებში,
ტექსტების დამუშავებასა და მრავალენოვან კომპიუტერულ სისტემებში.
Russian:
From a Unicode conference invitation:
Зарегистрируйтесь сейчас на Десятую Международную Конференцию по
Unicode, которая состоится 10-12 марта 1997 года в Майнце в Германии.
Конференция соберет широкий круг экспертов по вопросам глобального
Интернета и Unicode, локализации и интернационализации, воплощению и
применению Unicode в различных операционных системах и программных
приложениях, шрифтах, верстке и многоязычных компьютерных системах.
Thai (UCS Level 2):
Excerpt from a poetry on The Romance of The Three Kingdoms (a Chinese
classic 'San Gua'):
[----------------------------|------------------------]
๏ แผ่นดินฮั่นเสื่อมโทรมแสนสังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่
สิบสองกษัตริย์ก่อนหน้าแลถัดไป สององค์ไซร้โง่เขลาเบาปัญญา
ทรงนับถือขันทีเป็นที่พึ่ง บ้านเมืองจึงวิปริตเป็นนักหนา
โฮจิ๋นเรียกทัพทั่วหัวเมืองมา หมายจะฆ่ามดชั่วตัวสำคัญ
เหมือนขับไสไล่เสือจากเคหา รับหมาป่าเข้ามาเลยอาสัญ
ฝ่ายอ้องอุ้นยุแยกให้แตกกัน ใช้สาวนั้นเป็นชนวนชื่นชวนใจ
พลันลิฉุยกุยกีกลับก่อเหตุ ช่างอาเพศจริงหนาฟ้าร้องไห้
ต้องรบราฆ่าฟันจนบรรลัย ฤๅหาใครค้ำชูกู้บรรลังก์ ฯ
(The above is a two-column text. If combining characters are handled
correctly, the lines of the second column should be aligned with the
| character above.)
Ethiopian:
Proverbs in the Amharic language:
ሰማይ አይታረስ ንጉሥ አይከሰስ።
ብላ ካለኝ እንደአባቴ በቆመጠኝ።
ጌጥ ያለቤቱ ቁምጥና ነው።
ደሀ በሕልሙ ቅቤ ባይጠጣ ንጣት በገደለው።
የአፍ ወለምታ በቅቤ አይታሽም።
አይጥ በበላ ዳዋ ተመታ።
ሲተረጉሙ ይደረግሙ።
ቀስ በቀስ፥ ዕንቁላል በእግሩ ይሄዳል።
ድር ቢያብር አንበሳ ያስር።
ሰው እንደቤቱ እንጅ እንደ ጉረቤቱ አይተዳደርም።
እግዜር የከፈተውን ጉሮሮ ሳይዘጋው አይድርም።
የጎረቤት ሌባ፥ ቢያዩት ይስቅ ባያዩት ያጠልቅ።
ሥራ ከመፍታት ልጄን ላፋታት።
ዓባይ ማደሪያ የለው፥ ግንድ ይዞ ይዞራል።
የእስላም አገሩ መካ የአሞራ አገሩ ዋርካ።
ተንጋሎ ቢተፉ ተመልሶ ባፉ።
ወዳጅህ ማር ቢሆን ጨርስህ አትላሰው።
እግርህን በፍራሽህ ልክ ዘርጋ።
Runes:
ᚻᛖ ᚳᚹᚫᚦ ᚦᚫᛏ ᚻᛖ ᛒᚢᛞᛖ ᚩᚾ ᚦᚫᛗ ᛚᚪᚾᛞᛖ ᚾᚩᚱᚦᚹᛖᚪᚱᛞᚢᛗ ᚹᛁᚦ ᚦᚪ ᚹᛖᛥᚫ
(Old English, which transcribed into Latin reads 'He cwaeth that he
bude thaem lande northweardum with tha Westsae.' and means 'He said
that he lived in the northern land near the Western Sea.')
Braille:
⡌⠁⠧⠑ ⠼⠁⠒ ⡍⠜⠇⠑⠹⠰⠎ ⡣⠕⠌
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠙⠑⠁⠙⠒ ⠞⠕ ⠃⠑⠛⠔ ⠺⠊⠹⠲ ⡹⠻⠑ ⠊⠎ ⠝⠕ ⠙⠳⠃⠞
⠱⠁⠞⠑⠧⠻ ⠁⠃⠳⠞ ⠹⠁⠞⠲ ⡹⠑ ⠗⠑⠛⠊⠌⠻ ⠕⠋ ⠙⠊⠎ ⠃⠥⠗⠊⠁⠇ ⠺⠁⠎
⠎⠊⠛⠝⠫ ⠃⠹ ⠹⠑ ⠊⠇⠻⠛⠹⠍⠁⠝⠂ ⠹⠑ ⠊⠇⠻⠅⠂ ⠹⠑ ⠥⠝⠙⠻⠞⠁⠅⠻⠂
⠁⠝⠙ ⠹⠑ ⠡⠊⠑⠋ ⠍⠳⠗⠝⠻⠲ ⡎⠊⠗⠕⠕⠛⠑ ⠎⠊⠛⠝⠫ ⠊⠞⠲ ⡁⠝⠙
⡎⠊⠗⠕⠕⠛⠑⠰⠎ ⠝⠁⠍⠑ ⠺⠁⠎ ⠛⠕⠕⠙ ⠥⠏⠕⠝ ⠰⡡⠁⠝⠛⠑⠂ ⠋⠕⠗ ⠁⠝⠹⠹⠔⠛ ⠙⠑
⠡⠕⠎⠑ ⠞⠕ ⠏⠥⠞ ⠙⠊⠎ ⠙⠁⠝⠙ ⠞⠕⠲
⡕⠇⠙ ⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
⡍⠔⠙⠖ ⡊ ⠙⠕⠝⠰⠞ ⠍⠑⠁⠝ ⠞⠕ ⠎⠁⠹ ⠹⠁⠞ ⡊ ⠅⠝⠪⠂ ⠕⠋ ⠍⠹
⠪⠝ ⠅⠝⠪⠇⠫⠛⠑⠂ ⠱⠁⠞ ⠹⠻⠑ ⠊⠎ ⠏⠜⠞⠊⠊⠥⠇⠜⠇⠹ ⠙⠑⠁⠙ ⠁⠃⠳⠞
⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲ ⡊ ⠍⠊⠣⠞ ⠙⠁⠧⠑ ⠃⠑⠲ ⠔⠊⠇⠔⠫⠂ ⠍⠹⠎⠑⠇⠋⠂ ⠞⠕
⠗⠑⠛⠜⠙ ⠁ ⠊⠕⠋⠋⠔⠤⠝⠁⠊⠇ ⠁⠎ ⠹⠑ ⠙⠑⠁⠙⠑⠌ ⠏⠊⠑⠊⠑ ⠕⠋ ⠊⠗⠕⠝⠍⠕⠝⠛⠻⠹
⠔ ⠹⠑ ⠞⠗⠁⠙⠑⠲ ⡃⠥⠞ ⠹⠑ ⠺⠊⠎⠙⠕⠍ ⠕⠋ ⠳⠗ ⠁⠝⠊⠑⠌⠕⠗⠎
⠊⠎ ⠔ ⠹⠑ ⠎⠊⠍⠊⠇⠑⠆ ⠁⠝⠙ ⠍⠹ ⠥⠝⠙⠁⠇⠇⠪⠫ ⠙⠁⠝⠙⠎
⠩⠁⠇⠇ ⠝⠕⠞ ⠙⠊⠌⠥⠗⠃ ⠊⠞⠂ ⠕⠗ ⠹⠑ ⡊⠳⠝⠞⠗⠹⠰⠎ ⠙⠕⠝⠑ ⠋⠕⠗⠲ ⡹⠳
⠺⠊⠇⠇ ⠹⠻⠑⠋⠕⠗⠑ ⠏⠻⠍⠊⠞ ⠍⠑ ⠞⠕ ⠗⠑⠏⠑⠁⠞⠂ ⠑⠍⠏⠙⠁⠞⠊⠊⠁⠇⠇⠹⠂ ⠹⠁⠞
⡍⠜⠇⠑⠹ ⠺⠁⠎ ⠁⠎ ⠙⠑⠁⠙ ⠁⠎ ⠁ ⠙⠕⠕⠗⠤⠝⠁⠊⠇⠲
(The first couple of paragraphs of "A Christmas Carol" by Dickens)
Compact font selection example text:
ABCDEFGHIJKLMNOPQRSTUVWXYZ /0123456789
abcdefghijklmnopqrstuvwxyz £©µÀÆÖÞßéöÿ
–—‘“”„†•…‰™œŠŸž€ ΑΒΓΔΩαβγδω АБВГДабвгд
∀∂∈ℝ∧∪≡∞ ↑↗↨↻⇣ ┐┼╔╘░►☺♀ fi<>⑀₂ἠḂӥẄɐː⍎אԱა
Greetings in various languages:
Hello world, Καλημέρα κόσμε, コンニチハ
Box drawing alignment tests: █
╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
╠╡ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
▝▀▘▙▄▟

View File

@ -0,0 +1,18 @@
//
// IOTests.swift
// SocketRocket
//
// Created by Mike Lewis on 7/31/15.
//
//
enum FailureError: ErrorType {
case Failure
}
import XCTest
@testable import SocketRocketIO
class IOTests: XCTestCase {
}

View File

@ -0,0 +1,24 @@
<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</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>

View File

@ -0,0 +1,145 @@
//
// QueueStreamableTests.swift
// SocketRocket
//
// Created by Mike Lewis on 8/6/15.
//
//
import XCTest
@testable import SocketRocketIO
/*
class QueueStreamableTests: XCTestCase {
static let filePath = NSBundle(forClass: QueueStreamableTests.self).pathForResource("FileToReadFrom", ofType: "txt", inDirectory: "Fixtures")!
func testManualAccumulating() {
let io = dispatch_io_create_with_path(DISPATCH_IO_STREAM, QueueStreamableTests.filePath, O_RDONLY, 0, dispatch_queue_t.mainQueue.queue) { _ in }
let streamer = DispatchIO(io: io)
var accumulated = [UInt8]()
let readQueue = dispatch_queue_t(label: "Read queue")
let p = streamer.read(readQueue, size: Int.max) { data in
accumulated += data
}
expectationWithPromise(p)
dispatch_io_close(io, 0)
let readData = String(bytes:accumulated, encoding: NSUTF8StringEncoding)
XCTAssertEqual(readData!, "hello\nthis\nis a file\n\nI like to read from it")
}
func testErrorHandling() {
let io = dispatch_io_create_with_path(DISPATCH_IO_STREAM, QueueStreamableTests.filePath, O_RDONLY, 0, dispatch_queue_t.mainQueue.queue) { _ in }
let streamer = DispatchIO(io: io)
let readQueue = dispatch_queue_t(label: "Read queue")
dispatch_suspend(readQueue.queue)
let f = streamer.read(readQueue, size: Int.max) { v in
}
dispatch_io_close(streamer.io, DISPATCH_IO_STOP)
dispatch_resume(readQueue.queue)
expectationWithFailingPromise(f)
}
func testLoopback() {
let q = dispatch_queue_t(label: "loopbackQueue")
let l = Loopback<[UInt8]>(queue: q)
let p = l.readAll(q, collectionType: [UInt8].self).thenChecked { v in
let v = try v.checkedGet()
XCTAssertEqual(v, [UInt8]("OMG PONIESI LIKE TO EAT PONIES".utf8))
}
l.write(q, data: [UInt8]("OMG PONIES".utf8))
l.write(q, data: [UInt8]("I LIKE TO EAT PONIES".utf8))
expectationWithPromise(l.close(q), wait: false)
expectationWithPromise(p)
}
func testIOReadableWriteable() {
let acceptExpectation = expectationWithDescription("waiting")
let s = try! Socket.boundListeningSocket(sockaddr_in6.self, address: .Loopback, port: 0)
let q = dispatch_queue_t(label: "testIOReadableWriteable")
let (listenR, listenP) = Promise<Socket>.resolver(q)
let (cancelResolver, closedPromise) = s.startAccepting(dispatch_queue_t.mainQueue) {
(sock) -> Void in
listenR.resolve(sock)
acceptExpectation.fulfill()
}
let connectAddr = try! s.sockname()
let listenPort = connectAddr.port
XCTAssertNotEqual(listenPort, 0)
let clientIOPromise: Promise<Void> = Socket.tryConnect(q, sockAddr: connectAddr)
.thenChecked { v -> DispatchIO in
let sock = try v.checkedGet()
let io = dispatch_io_create(DISPATCH_IO_STREAM, sock.fd, q.queue, { _ in try! sock.close() })
return DispatchIO(io: io)
}
.thenSplit { io in
io.write(q, data: [UInt8]("OMG PONIES".utf8))
io.write(q, data: [UInt8]("I LIKE TO EAT PONIES".utf8))
return .Promised(io.close(q))
}
var serverIO: DispatchIO! = nil
let serverIOPromise: Promise<Void> = listenP
.thenChecked { v -> DispatchIO in
let sock = try v.checkedGet()
let io = dispatch_io_create(DISPATCH_IO_STREAM, sock.fd, q.queue, { _ in try! sock.close() })
return DispatchIO(io: io)
}
.thenSplit { io -> PromiseOrValue<[UInt8]> in
serverIO = io
return .Promised(io.readAll(q, collectionType: [UInt8].self))
}
.thenChecked { v in
let v = try v.checkedGet()
XCTAssertEqual(v, [UInt8]("OMG PONIESI LIKE TO EAT PONIES".utf8))
}
.thenSplit { v in
return .Promised(serverIO.close(q))
}
self.expectationWithPromise(serverIOPromise, wait:false)
self.expectationWithPromise(clientIOPromise, wait:false)
waitForExpectations()
cancelResolver.resolve()
self.expectationWithPromise(closedPromise)
}
}
*/

View File

@ -0,0 +1,213 @@
//
// SocketTests.swift
// SocketRocket
//
// Created by Mike Lewis on 8/11/15.
//
//
import XCTest
@testable import SocketRocketIO
/*
class SocketTests: XCTestCase {
let sockaddrTypes: [SockAddr.Type] = [sockaddr_in.self, sockaddr_in6.self]
func testGetAddrInfoAsync() {
let promise: Promise<[AddrInfo]> = AddrInfo.getAddrInfo(dispatch_queue_t.mainQueue, hostname: "squareup.com", servname: "443")
.thenChecked { v in
let v = try v.checkedGet()
XCTAssertGreaterThan(v.count, 0)
return v
}
.thenChecked(dispatch_queue_t.mainQueue) { (v: ErrorOptional<[AddrInfo]>) throws in
guard dispatch_queue_t.mainQueue.isCurrentQueue() else {
throw FailureError.Failure
}
return try v.checkedGet()
}
self.expectationWithPromise(promise)
}
func testGetAddrInfoAsyncFailure() {
let promise: Promise<[AddrInfo]> = AddrInfo.getAddrInfo(dispatch_queue_t.mainQueue, hostname: "", servname: "")
self.expectationWithFailingPromise(promise)
}
func testListen() {
for t in sockaddrTypes {
let acceptExpectation = expectationWithDescription("waiting")
let s = try! Socket.boundListeningSocket(t, address: .Loopback, port: 0)
let q = dispatch_queue_t(label: "testListen")
let (cancelResolver, closedPromise) = s.startAccepting(dispatch_queue_t.mainQueue) {
(sock) -> Void in
try! sock.close()
acceptExpectation.fulfill()
}
let connectAddr = try! s.sockname()
let listenPort = connectAddr.port
XCTAssertNotEqual(listenPort, 0)
let connectPromise = Socket.tryConnect(q, sockAddr: connectAddr)
.thenChecked { v in
try v.checkedGet().close()
}
self.expectationWithPromise(connectPromise, wait:false)
waitForExpectations()
cancelResolver.resolve()
self.expectationWithPromise(closedPromise)
}
}
func testListenIPV6Name() {
let acceptExpectation = expectationWithDescription("waiting")
let s = try! Socket.boundListeningSocket(sockaddr_in6.self, address: .IPv6Addr(address: "::1"), port: 0)
let q = dispatch_queue_t(label: "testListen")
let (cancelResolver, closedPromise) = s.startAccepting(dispatch_queue_t.mainQueue) {
(sock) -> Void in
try! sock.close()
acceptExpectation.fulfill()
}
let connectAddr = try! s.sockname()
let listenPort = connectAddr.port
XCTAssertNotEqual(listenPort, 0)
let connectPromise = Socket.tryConnect(q, sockAddr: connectAddr)
.thenChecked { v in
try v.checkedGet().close()
}
self.expectationWithPromise(connectPromise, wait:false)
waitForExpectations()
cancelResolver.resolve()
self.expectationWithPromise(closedPromise)
}
func testListenIPV4Name() {
let acceptExpectation = expectationWithDescription("waiting")
let s = try! Socket.boundListeningSocket(sockaddr_in.self, address: .IPv4Addr(address: "127.0.0.1"), port: 0)
let q = dispatch_queue_t(label: "testListen")
let (cancelResolver, closedPromise) = s.startAccepting(dispatch_queue_t.mainQueue) {
(sock) -> Void in
try! sock.close()
acceptExpectation.fulfill()
}
let connectAddr = try! s.sockname()
let listenPort = connectAddr.port
XCTAssertNotEqual(listenPort, 0)
let connectPromise = Socket.tryConnect(q, sockAddr: connectAddr)
.thenChecked { v in
try v.checkedGet().close()
}
self.expectationWithPromise(connectPromise, wait:false)
waitForExpectations()
cancelResolver.resolve()
self.expectationWithPromise(closedPromise)
}
func testListen_multiconnect() {
for t in sockaddrTypes {
let acceptExpectation = expectationWithDescription("waiting")
let s = try! Socket.boundListeningSocket(t, address: .Loopback, port: 0)
let q = dispatch_queue_t(label: "testListen")
let (cancelResolver, closedPromise) = s.startAccepting(dispatch_queue_t.mainQueue) {
(sock) -> Void in
try! sock.close()
acceptExpectation.fulfill()
}
let connectAddr = try! s.sockname()
let listenPort = connectAddr.port
XCTAssertNotEqual(listenPort, 0)
let connectPromise = AddrInfo.getAddrInfo(q, hostname: "localhost", servname: "\(listenPort)")
.thenSplit(q) { v -> PromiseOrValue<Socket> in
return .Promised(Socket.tryConnect(q, sockAddrs: v.map { ai in return ai.sockAddr}))
}.thenChecked { v in
try v
.checkedGet()
.close()
}
self.expectationWithPromise(connectPromise, wait:false)
waitForExpectations()
cancelResolver.resolve()
self.expectationWithPromise(closedPromise)
}
}
func testTryConnect() {
for t in sockaddrTypes {
let acceptExpectation = expectationWithDescription("waiting")
let s = try! Socket.boundListeningSocket(t, address: .Loopback, port: 0)
let q = dispatch_queue_t(label: "testListen")
let (cancelResolver, closedPromise) = s.startAccepting(dispatch_queue_t.mainQueue) {
(sock) -> Void in
try! sock.close()
acceptExpectation.fulfill()
}
let connectAddr = try! s.sockname()
let listenPort = connectAddr.port
XCTAssertNotEqual(listenPort, 0)
let connectPromise = Socket.tryConnect(q, hostname: "localhost", port: listenPort)
.thenChecked { v in
try v
.checkedGet()
.close()
}
self.expectationWithPromise(connectPromise, wait:false)
waitForExpectations()
cancelResolver.resolve()
self.expectationWithPromise(closedPromise)
}
}
}
*/

View File

@ -0,0 +1,171 @@
//
// UnicodeTests.swift
// SocketRocket
//
// Created by Mike Lewis on 8/6/15.
//
//
import XCTest
@testable import SocketRocketIO
struct SourceInfo {
let line: UInt
let file: String
init(line: UInt = __LINE__, file: String = __FILE__) {
self.line = line
self.file = file
}
}
typealias SI = SourceInfo
class UnicodeTests: XCTestCase {
func testCodeUnits() {
let s = "💩1💩"
var data = [UInt8](s.utf8)
XCTAssertEqualT(try UTF8.numValidCodeUnits(data.generate()), 9)
XCTAssertEqualT(try UTF8.numValidCodeUnits(data[0..<8].generate()), 5)
XCTAssertEqualT(try UTF8.numValidCodeUnits(data[0..<5].generate()), 5)
XCTAssertEqualT(try UTF8.numValidCodeUnits(data[0..<4].generate()), 4)
XCTAssertEqualT(try UTF8.numValidCodeUnits(data[0..<3].generate()), 0)
}
func testDecoding() {
var uc = UTF8()
let s = "💩1💩"
var data = [UInt8]()
for s in s.unicodeScalars {
UTF8.encode(s) { (cu) -> () in
data.append(cu)
}
}
XCTAssertEqual(data.count, 9)
var c = RawUTF8Codec()
var buff = String()
func accumulate(slice: ArraySlice<UInt8>, line: UInt = __LINE__, file: String = __FILE__) {
do {
let val = ValueOrEnd<ArraySlice<UInt8>>.Value(slice)
try c.code(val, output: &buff.unicodeScalars)
} catch let e {
XCTFail("\(e)", line:line, file:file)
}
}
accumulate(data[0..<2])
accumulate(data[2..<3])
accumulate(data[3..<8])
accumulate(data[8..<9])
do {
try c.code(ValueOrEnd<ArraySlice<UInt8>>.End, output: &buff.unicodeScalars)
} catch let e {
XCTFail("\(e)")
}
XCTAssertEqual(buff, s)
}
func testDecoding_errorHandling() {
var uc = UTF8()
let s = "💩1💩"
var data = [UInt8](s.utf8)
XCTAssertEqual(data.count, 9)
var c = RawUTF8Codec()
var buff = String()
func accumulate(slice: ArraySlice<UInt8>, line: UInt = __LINE__, file: String = __FILE__) {
do {
let val = ValueOrEnd<ArraySlice<UInt8>>.Value(slice)
try c.code(val, output: &buff.unicodeScalars)
} catch let e {
XCTFail("\(e)", line:line, file:file)
}
}
accumulate(data[0..<2])
accumulate(data[2..<3])
accumulate(data[3..<8])
do {
try c.code(ValueOrEnd<ArraySlice<UInt8>>.End, output: &buff.unicodeScalars)
XCTFail("Should except")
} catch {
}
}
func testDecoding_Smoke() {
let filePath = NSBundle(forClass: UnicodeTests.self).pathForResource("UTF8Sample", ofType: "txt", inDirectory: "Fixtures")!
let s = NSData(contentsOfFile: filePath)!
let nominalString = NSString(data: s, encoding: NSUTF8StringEncoding) as! String
doTestAccumulateSplit(nominalString, splitPoints: [Int](0..<(s.length - 1)))
doTestAccumulateSplit(nominalString, splitPoints: [])
doTestAccumulateSplit(nominalString, splitPoints: [Int](0..<((s.length - 1) / 2)).map({e in return e / 2}))
}
func doTestAccumulateSplit(data: [UInt8], var splitPoints: [Int], si: SI = SI()) -> String {
var c = RawUTF8Codec()
var buff = String()
func accumulate(slice: ArraySlice<UInt8>, si: SI) {
do {
let val = ValueOrEnd<ArraySlice<UInt8>>.Value(slice)
try c.code(val, output: &buff.unicodeScalars)
} catch let e {
XCTFail("\(e)")
XCTFail("\(e) (from here)", line:si.line, file:si.file)
}
}
splitPoints.sortInPlace()
var lastIdx = data.startIndex
for sp in splitPoints {
let newIdx = data.startIndex.advancedBy(sp)
accumulate(data[lastIdx..<newIdx], si: si)
lastIdx = newIdx
}
accumulate(data[lastIdx..<data.endIndex], si: si)
do {
try c.code(ValueOrEnd<ArraySlice<UInt8>>.End, output: &buff.unicodeScalars)
} catch let e {
XCTFail("Should not except \(e)")
}
return buff
}
func doTestAccumulateSplit(string: String, splitPoints: [Int], si: SI = SI()) {
var data = [UInt8]()
for s in string.unicodeScalars {
UTF8.encode(s) { (cu) -> () in
data.append(cu)
}
}
let r = doTestAccumulateSplit(data, splitPoints: splitPoints, si: si)
XCTAssertEqual(string, r, line:si.line, file:si.file)
}
}

View File

@ -0,0 +1,108 @@
//
// Util.swift
// SocketRocket
//
// Created by Mike Lewis on 1/6/16.
//
//
import Foundation
import XCTest
import RxSwift
func XCTAssertEqualT<T : Equatable>(@autoclosure expression1: () throws -> T, @autoclosure _ expression2: () -> T, _ message: String = "", file: String = __FILE__, line: UInt = __LINE__){
do {
let v = try expression1()
XCTAssertEqual(v, expression2(), line: line, file:file)
} catch let e {
XCTFail("\(e)", line: line, file:file)
}
}
extension POSIXError: CustomDebugStringConvertible {
public var debugDescription: String {
get {
var buffer = [CChar](count: 1024, repeatedValue: 0)
guard strerror_r(rawValue, &buffer, buffer.count) == 0 else {
return "Unknown!!"
}
return "Posix Error \(rawValue) " + (String.fromCString(&buffer) ?? "Issue converting to cstring")
}
}
}
var defaultWaitTimeout: NSTimeInterval = 10.0
/// Wraps a test case and an observable to block on the result and wait for values
public class ObservableFuture<E> {
let description: String
let testCase: XCTestCase /// Where we were created
let disposeBag = DisposeBag()
let replayObservable: ConnectableObservable<E>
private init<O: ObservableType where O.E == E>(description: String, observable: O, testCase: XCTestCase) {
self.description = description
self.testCase = testCase
replayObservable = observable.replay(1024)
replayObservable.connect().addDisposableTo(disposeBag)
}
/// Call this to wait for the expectation and get the value
public func get(timeout timeout: NSTimeInterval=defaultWaitTimeout, file: String=__FILE__, line: UInt=__LINE__) -> [E] {
let expectation = testCase.expectationWithDescription(description)
var result = [E]()
replayObservable
.subscribeOn(MainScheduler.instance)
.subscribe(
onNext: {
val in result.append(val)
},
onError: { e in
XCTFail("Error with observable \(Mirror(reflecting: e).description)", file: file, line: line)
},
onCompleted: { expectation.fulfill() })
.addDisposableTo(disposeBag)
testCase.waitForExpectations(timeout)
return result
}
}
public extension SequenceType where Generator.Element == UInt8 {
// Converts to a string w/ UTF8 data
var utf8String: String? {
// Append a zero to ourselves
let buff = Array(self) + [0]
return buff.withUnsafeBufferPointer { ptr in
return String.fromCString(unsafeBitCast(ptr.baseAddress, UnsafePointer<Int8>.self))
}
}
}
extension ObservableType {
/// Makes a Future out of an observable. It requires a test since it depends on waiting for expectations.
/// It starts recording immediately. One should call get on the resultss
@warn_unused_result
public func subscribeFuture(test: XCTestCase, description: String? = nil) -> ObservableFuture<E> {
let description = description ?? "Completion of observable \(Mirror(reflecting: self).description)"
return ObservableFuture(description: description, observable: self, testCase: test)
}
}
extension XCTestCase {
/// Making it so you can easily wait for expectation
public func waitForExpectations(timeout: NSTimeInterval=defaultWaitTimeout) {
self.waitForExpectationsWithTimeout(timeout, handler: nil)
}
}

View File

@ -9,7 +9,7 @@
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.squareup.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -0,0 +1,24 @@
<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</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>

View File

@ -0,0 +1,271 @@
//
// WebSocketProtocolTest.swift
// SocketRocket
//
// Created by Mike Lewis on 1/7/16.
//
//
import Foundation
import XCTest
import RxSwift
@testable import SocketRocket
@testable import SocketRocketIO
/// Example frames from the RFC
struct ExampleFrames {
/// A single-frame unmasked text message
/// contains "Hello"
static let singleFrameUnmasked: [UInt8] = [0x81, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
// A single-frame masked text message
static let singleFrameMasked: [UInt8] = [0x81,0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58]
// A fragmented unmasked text message
// Contains "Hel" then "lo"
static let fragmentedUnmasked: [UInt8] =
[0x01, 0x03, 0x48, 0x65, 0x6c] +
[0x80, 0x02, 0x6c, 0x6f]
/// Unmasked Ping request
static let unmaskedPingRequest: [UInt8] = [0x89, 0x05, 0x48, 0x65, 0x6c, 0x6c, 0x6f]
/// Unmasked Ping response
static let maskedPingResponse: [UInt8] = [0x8a, 0x85, 0x37, 0xfa, 0x21, 0x3d, 0x7f, 0x9f, 0x4d, 0x51, 0x58]
static let shortBinaryMessageContents = (0..<256).map { UInt8($0 % 0xFF) }
/// Binary message that is 256 long
static let shortBinaryMessage: [UInt8] = [0x82, 0x7E, 0x01, 0x00] + shortBinaryMessageContents
static let longBinaryMessageContents = (0..<65536).map { UInt8($0 % 0xFF) }
/// Binary message that is 65536 long
static let longBinaryMessage: [UInt8] = [0x82, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00] + longBinaryMessageContents
}
class WebSocketProtocolTests : XCTestCase {
func testSingleFrameUnmasked() {
let frame = readFrame(ExampleFrames.singleFrameUnmasked)
XCTAssertEqual(frame.opCode, .Text)
XCTAssertTrue(frame.header.fin)
XCTAssertFalse(frame.header.mask)
XCTAssertNil(frame.header.maskingKey)
XCTAssertEqual(frame.payload.utf8String, "Hello")
}
func testSingleFrameMasked() {
/// TODO: Unmask frame data
let frame = readFrame(ExampleFrames.singleFrameMasked)
XCTAssertEqual(frame.opCode, .Text)
XCTAssertTrue(frame.header.fin)
XCTAssertTrue(frame.header.mask)
XCTAssertNotNil(frame.header.maskingKey)
XCTAssertEqual(frame.payload.utf8String, "Hello")
}
func testFragmentedUnmasked() {
/// TODO: Unmask frame data
let frames = readFrames(ExampleFrames.fragmentedUnmasked)
XCTAssertEqual(frames.count, 2)
let frame1 = frames[0]
let frame2 = frames[1]
XCTAssertEqual(frame1.opCode, .Text)
XCTAssertFalse(frame1.header.fin)
XCTAssertFalse(frame1.header.mask)
XCTAssertNil(frame1.header.maskingKey)
XCTAssertEqual(frame1.payload.utf8String, "Hel")
XCTAssertEqual(frame2.opCode, .Continuation)
XCTAssertTrue(frame2.header.fin)
XCTAssertFalse(frame2.header.mask)
XCTAssertNil(frame2.header.maskingKey)
XCTAssertEqual(frame2.payload.utf8String, "lo")
}
func testPingUnmasked() {
/// TODO: Unmask frame data
let frame = readFrame(ExampleFrames.unmaskedPingRequest)
XCTAssertEqual(frame.opCode, .Ping)
XCTAssertEqual(frame.payload.utf8String, "Hello")
XCTAssertTrue(frame.header.fin)
XCTAssertFalse(frame.header.mask)
XCTAssertNil(frame.header.maskingKey)
}
func testPongUnmasked() {
let frame = readFrame(ExampleFrames.maskedPingResponse)
XCTAssertEqual(frame.opCode, .Pong)
XCTAssertEqual(frame.payload.utf8String, "Hello")
XCTAssertTrue(frame.header.fin)
XCTAssertTrue(frame.header.mask)
XCTAssertNotNil(frame.header.maskingKey)
}
func testShortMessageUnmasked() {
/// TODO: Unmask frame data
let frame = readFrame(ExampleFrames.shortBinaryMessage)
XCTAssertEqual(frame.payload, ExampleFrames.shortBinaryMessageContents)
XCTAssertEqual(frame.opCode, .Binary)
XCTAssertTrue(frame.header.fin)
XCTAssertFalse(frame.header.mask)
XCTAssertNil(frame.header.maskingKey)
}
func testLongMessageUnmasked() {
/// TODO: Unmask frame data
let frame = readFrame(ExampleFrames.longBinaryMessage)
XCTAssertEqual(frame.payload, ExampleFrames.longBinaryMessageContents)
XCTAssertEqual(frame.opCode, .Binary)
XCTAssertTrue(frame.header.fin)
XCTAssertFalse(frame.header.mask)
XCTAssertNil(frame.header.maskingKey)
}
private func readFrame(data: [UInt8], file: String = __FILE__, line: UInt = __LINE__) -> MaterializedWebsocketFrame {
let vals = self.readFrames(data, file: file, line: line)
XCTAssertEqual(vals.count, 1)
return vals.first!
}
private func readFramesFromStream<I: InputStream where I.Element == UInt8>(stream: I) -> [MaterializedWebsocketFrame] {
let frameReader = WebSocketFrameReader(stream: stream)
let frames = frameReader
.readFrames(MainScheduler.instance)
.flatMap { $0.materialized() }
.subscribeFuture(self)
.get(timeout: 600)
for f in frames {
XCTAssertTrue(f.header.isComplete)
}
return frames
}
private func readFrames(data: [UInt8], file: String = __FILE__, line: UInt = __LINE__) -> [MaterializedWebsocketFrame] {
let eagerStream1Chunk = Observable
.just(data)
.asInputStream()
let eagerStreamSeveralChunk = data
.toObservable()
.toArray()
.asInputStream()
let queue = dispatch_queue_create("testQueue", DISPATCH_QUEUE_SERIAL)
let lazySingleObservable: Observable<[UInt8]> = Observable.create { observer in
dispatch_async(queue) {
observer.onNext(data)
observer.onCompleted()
}
return NopDisposable.instance
}
let lazyIndividualObservable: Observable<[UInt8]> = Observable.create { observer in
for byte in data {
dispatch_async(queue) {
observer.onNext([byte])
}
}
dispatch_async(queue) {
observer.onCompleted()
}
return NopDisposable.instance
}
/// We're going to try multiple different ways to produce this data
let streams: [AnyInputStream<UInt8>] = [
anyInputStream(eagerStream1Chunk),
anyInputStream(eagerStreamSeveralChunk),
anyInputStream(lazySingleObservable.asInputStream()),
anyInputStream(lazyIndividualObservable.asInputStream()),
]
let frames = streams
.map(readFramesFromStream)
for f in frames.flatten() {
XCTAssertEqual(f.payload.count, f.header.payloadLen)
}
var lastFrames: [MaterializedWebsocketFrame]?
for (i, f) in frames.enumerate() {
if let last = lastFrames {
/// Fail twice so we know which test is bad too
XCTAssertEqual(last, f, "\(i) stream failed", file:file, line:line)
XCTAssertEqual(last, f, "\(i) stream failed")
}
lastFrames = f
}
return frames.first!
}
}
extension WebSocketFrame {
func materialized() -> Observable<MaterializedWebsocketFrame> {
return Observable.create { observer in
var buffer = [UInt8]()
let header = self.header
return self
.payload
.subscribe { event in
switch event {
case .Completed:
let materialized = MaterializedWebsocketFrame(header: header, payload: buffer)
observer.onNext(materialized)
observer.onCompleted()
case let .Error(e):
observer.onError(e)
case let .Next(val):
buffer.appendContentsOf(val)
}
}
}
}
}
/// A websocket frame that is comparable that has the data consumed already.
struct MaterializedWebsocketFrame : Equatable {
let header: WebSocketFrameHeader
let payload: [UInt8]
var opCode: WebSocketOpcode? {
return WebSocketOpcode(rawValue: self.header.opcode)
}
}
func == (lhs: MaterializedWebsocketFrame, rhs: MaterializedWebsocketFrame) -> Bool {
return lhs.header == rhs.header && lhs.payload == rhs.payload
}

26
SystemShims/Info.plist Normal file
View File

@ -0,0 +1,26 @@
<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

45
SystemShims/SystemShims.h Normal file
View File

@ -0,0 +1,45 @@
//
// SystemShims.h
// SystemShims
//
// Created by Mike Lewis on 7/31/15.
//
//
#import <UIKit/UIKit.h>
//! Project version number for SystemShims.
FOUNDATION_EXPORT double SystemShimsVersionNumber;
//! Project version string for SystemShims.
FOUNDATION_EXPORT const unsigned char SystemShimsVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <SystemShims/PublicHeader.h>
extern int shim_fcntl(int fildes, int cmd, int flags);
extern int shim_bind(int fildes, const void *addr, size_t size);
extern int shim_accept(int fildes, void *addr, socklen_t *size);
#include <netinet/in.h>
typedef union sockaddr_union {
struct sockaddr addr;
struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
} sockaddr_union;
// Returns sockaddr from union
extern inline struct sockaddr *sockaddr_union_getsockaddr(sockaddr_union *u) {
return &u->addr;
}
extern inline const struct sockaddr_in *sockaddr_union_getsockaddr_in(const sockaddr_union *u) {
return &u->ipv4;
}
extern inline const struct sockaddr_in6 *sockaddr_union_getsockaddr_in6(const sockaddr_union *u) {
return &u->ipv6;
}

24
SystemShims/SystemShims.m Normal file
View File

@ -0,0 +1,24 @@
//
// SystemShims.c
// SocketRocket
//
// Created by Mike Lewis on 7/31/15.
//
//
#import "SystemShims.h"
#import <sys/fcntl.h>
#import <sys/socket.h>
extern int shim_fcntl(int fildes, int cmd, int flags) {
return fcntl(fildes, cmd, flags);
}
extern int shim_bind(int fildes, const void *addr, size_t size) {
return bind(fildes, addr, (socklen_t)size);
}
extern int shim_accept(int fildes, void *addr, socklen_t *size) {
return accept(fildes, addr, size);
}

View File

@ -1,61 +0,0 @@
//
// TCAppDelegate.m
// TestChat
//
// Created by Mike Lewis on 1/28/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "TCAppDelegate.h"
@implementation TCAppDelegate
@synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
/*
Called when the application is about to terminate.
Save data if appropriate.
See also applicationDidEnterBackground:.
*/
}
@end

130
TestChat/TCAppDelegate.mm Normal file
View File

@ -0,0 +1,130 @@
//
// TCAppDelegate.m
// TestChat
//
// Created by Mike Lewis on 1/28/12.
// Copyright (c) 2012 __MyCompanyName__. All rights reserved.
//
#import "TCAppDelegate.h"
#import <Security/SecureTransport.h>
#import "SecureIO.h"
#import <string>
using namespace squareup::dispatch;
@implementation TCAppDelegate {
SecureIO *_io;
}
@synthesize window = _window;
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
return YES;
}
- (void)applicationWillResignActive:(UIApplication *)application
{
/*
Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
/*
Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
*/
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
/*
Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
*/
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
SSLContextRef ctx = SSLCreateContext(CFAllocatorGetDefault(), kSSLClientSide, kSSLStreamType);
auto finishBlock = [self](int error) {
NSLog(@"Done");
};
dispatch_queue_t workQueue = dispatch_queue_create("squareup.dispatch work queue", DISPATCH_QUEUE_SERIAL);
DialTLS("localhost", "10248", ctx, dispatch_get_main_queue(), workQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), [self, _cmd](squareup::dispatch::SecureIO *io, int error, const char *error_message) {
NSAssert(error == 0, @"Should not have errored, but got %s", error_message);
NSAssert(io != nullptr, @"io should be valid");
if (!io) {
return;
}
_io = io;
// __block bool seenInner = false;
// __block bool seenOuter = false;
//
// _io->Write(Data("HELLO THERE!\n", dispatch_get_main_queue()), ^(bool done, dispatch_data_t data, int error) {
// STAssertEquals(error, 0, @"Error should == 0");
// STAssertFalse(seenOuter, @"Should only see the outer once");
// if (done) {
// seenOuter = true;
// }
// if (done && !error) {
//
// }
// });
//
// _io->Write(Data("HELLO THERE2!\n", dispatch_get_main_queue()), ^(bool done, dispatch_data_t data, int error) {
// STAssertFalse(seenInner, @"Shouldn't have seen inner yet");
// if (done) {
// seenInner = done;
// }
// STAssertEquals(error, 0, @"Error should == 0");
// STAssertFalse(finished, @"Shouldn't have finished");
// });
for (int i = 0; i < 4096; i++) {
_io->Read(1024 * 1024* 13, dispatch_get_main_queue(), [self, _cmd, i](bool readDone, dispatch_data_t data, int error) {
if (!error) {
_io->Write(data, dispatch_get_main_queue(), [self, readDone, i](bool done, dispatch_data_t data, int error) {
});
} else {
NSAssert(error == ECANCELED, @"Server should terminate");
if (readDone && !error) {
_io->Close(0);
}
}
});
}
}, finishBlock);
/*
Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
*/
}
- (void)applicationWillTerminate:(UIApplication *)application
{
/*
Called when the application is about to terminate.
Save data if appropriate.
See also applicationDidEnterBackground:.
*/
}
@end

View File

@ -11,7 +11,7 @@
<key>CFBundleIconFiles</key>
<array/>
<key>CFBundleIdentifier</key>
<string>com.squareup.${PRODUCT_NAME:rfc1034identifier}</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>

View File

@ -0,0 +1,96 @@
package main
import (
"bytes"
"crypto/rand"
"crypto/tls"
"io"
"io/ioutil"
"log"
"net"
)
func main() {
keyPair, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
panic(err)
}
cfg := &tls.Config{
Certificates: []tls.Certificate{keyPair},
}
listener, err := tls.Listen("tcp", ":10248", cfg)
if err != nil {
panic(err)
}
for conn, err := listener.Accept(); err == nil; conn, err = listener.Accept() {
go func(conn net.Conn) {
defer func() {
if err := recover(); err != nil {
log.Println("Something failed:", err)
}
}()
ch := make(chan bool, 1)
maxPending := make(chan bool, 2)
chunkSize := int64(1024 * 1024)
buff2 := bytes.NewBuffer(make([]byte, 0, chunkSize))
randBytes := make([]byte, chunkSize)
_, err := rand.Read(randBytes)
if err != nil {
panic(err)
}
randReader := bytes.NewReader(randBytes)
ch <- true
go func() {
defer func() {
if err := recover(); err != nil {
log.Println("Something failed:", err)
}
}()
defer func() {
<-ch
}()
for i := 0; ; i++ {
defer func() {
if err := recover(); err != nil {
log.Println("Something failed:", err)
}
}()
_, err := io.CopyN(ioutil.Discard, conn, chunkSize)
if err != nil {
panic(err)
}
<-maxPending
log.Println("Chunk read back (", i, ")")
}
buff2.Reset()
}()
for {
maxPending <- true
randReader.Seek(0, 0)
_, err := io.CopyN(conn, randReader, chunkSize)
if err != nil {
panic(err)
}
}
ch <- true
conn.Close()
}(conn)
}
if err != nil {
panic(err)
}
}