Compare commits
28 Commits
master
...
lewis/rxsw
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cefb83328 | ||
|
|
15caaa0cc9 | ||
|
|
0266b0b7f4 | ||
|
|
acd07c14de | ||
|
|
95de1ea335 | ||
|
|
e3c402beab | ||
|
|
f616386954 | ||
|
|
61b19ab633 | ||
|
|
c62bfb90ad | ||
|
|
d7078b082c | ||
|
|
49c79072d2 | ||
|
|
a797a0f568 | ||
|
|
7c6e18e858 | ||
|
|
e6c3850c9b | ||
|
|
fe572dc752 | ||
|
|
9b0d5134f8 | ||
|
|
1883c521d6 | ||
|
|
5214ed5347 | ||
|
|
7ea9d25976 | ||
|
|
5278664ab7 | ||
|
|
2a2ed772c3 | ||
|
|
d237938434 | ||
|
|
ac861d6ff2 | ||
|
|
2ab4b7d421 | ||
|
|
edd6faceed | ||
|
|
f58b21618c | ||
|
|
fdf93c6f9c | ||
|
|
72f7f81ff4 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -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
1
Frameworks/RxSwift
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 1c47f0a79231f889a6c954023d857a3b5344ac88
|
||||
@ -107,6 +107,7 @@
|
||||
|
||||
- (NSUInteger)testCaseCount;
|
||||
{
|
||||
return 0;
|
||||
if (self.invocation) {
|
||||
return [super testCaseCount];
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
13
SRWebSocketTests/STRDispatchTest.h
Normal file
13
SRWebSocketTests/STRDispatchTest.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// STRDispatchTest.h
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Michael Lewis on 1/12/13.
|
||||
//
|
||||
//
|
||||
|
||||
#import <SenTestingKit/SenTestingKit.h>
|
||||
|
||||
@interface STRDispatchTest : SenTestCase
|
||||
|
||||
@end
|
||||
151
SRWebSocketTests/STRDispatchTest.mm
Normal file
151
SRWebSocketTests/STRDispatchTest.mm
Normal 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
|
||||
@ -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
878
SocketRocket.xcodeproj/project.pbxproj.orig
Normal file
878
SocketRocket.xcodeproj/project.pbxproj.orig
Normal 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 */;
|
||||
}
|
||||
@ -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 if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing Dangling SRTextharneess PID:" $EXISTING_PID kill $EXISTING_PID rm $PIDFILE fi pushd $PROJECT_DIR export MACOSX_DEPLOYMENT_TARGET= bash TestSupport/ensure_virtualenv.sh $PROJECT_DIR/.env source .env/bin/activate rm -rf "$PROJECT_DIR/reports/clients/" nohup sr-testharness -i '' -c '*' & #nohup sr-testharness -k hello_test_harness -i '' -c '*' & echo $! > $PIDFILE 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 if [ -r $PIDFILE ]; then EXISTING_PID=`cat $PIDFILE` echo "Killing SRTextharneess PID:" $EXISTING_PID kill $EXISTING_PID rm $PIDFILE fi 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">
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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
32
SocketRocket/Common.h
Normal 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
|
||||
77
SocketRocket/Definitions.swift
Normal file
77
SocketRocket/Definitions.swift
Normal 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
149
SocketRocket/DispatchData.h
Normal 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__) */
|
||||
15
SocketRocket/DispatchData.mm
Normal file
15
SocketRocket/DispatchData.mm
Normal 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
91
SocketRocket/DispatchIO.h
Normal 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
298
SocketRocket/DispatchIO.mm
Normal 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
26
SocketRocket/Info.plist
Normal 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>
|
||||
@ -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)];
|
||||
1768
SocketRocket/SRWebSocket.mm.orig
Normal file
1768
SocketRocket/SRWebSocket.mm.orig
Normal file
File diff suppressed because it is too large
Load Diff
176
SocketRocket/SecureIO.h
Normal file
176
SocketRocket/SecureIO.h
Normal 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
475
SocketRocket/SecureIO.mm
Normal 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);
|
||||
};
|
||||
}
|
||||
}
|
||||
19
SocketRocket/SocketRocket.h
Normal file
19
SocketRocket/SocketRocket.h
Normal 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>
|
||||
|
||||
|
||||
10
SocketRocket/WebSocket.swift
Normal file
10
SocketRocket/WebSocket.swift
Normal file
@ -0,0 +1,10 @@
|
||||
////
|
||||
//// WebSocket.swift
|
||||
//// SocketRocket
|
||||
////
|
||||
//// Created by Mike Lewis on 1/6/16.
|
||||
////
|
||||
////
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
299
SocketRocket/WebSocketProtocol.swift
Normal file
299
SocketRocket/WebSocketProtocol.swift
Normal 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
|
||||
}
|
||||
}
|
||||
387
SocketRocketIO/BufferedInputStream.swift
Normal file
387
SocketRocketIO/BufferedInputStream.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
68
SocketRocketIO/Codec.swift
Normal file
68
SocketRocketIO/Codec.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
SocketRocketIO/Dial.swift
Normal file
9
SocketRocketIO/Dial.swift
Normal file
@ -0,0 +1,9 @@
|
||||
//
|
||||
// Dial.swift
|
||||
// SocketRocket
|
||||
//
|
||||
// Created by Mike Lewis on 1/5/16.
|
||||
//
|
||||
//
|
||||
|
||||
import Foundation
|
||||
83
SocketRocketIO/DispatchIO.swift
Normal file
83
SocketRocketIO/DispatchIO.swift
Normal 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
|
||||
}
|
||||
}
|
||||
57
SocketRocketIO/Error.swift
Normal file
57
SocketRocketIO/Error.swift
Normal 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
|
||||
}
|
||||
}
|
||||
53
SocketRocketIO/Extensions.swift
Normal file
53
SocketRocketIO/Extensions.swift
Normal 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
26
SocketRocketIO/Info.plist
Normal 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>
|
||||
64
SocketRocketIO/Locks.swift
Normal file
64
SocketRocketIO/Locks.swift
Normal 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()
|
||||
}
|
||||
}
|
||||
181
SocketRocketIO/Loopback.swift
Normal file
181
SocketRocketIO/Loopback.swift
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
106
SocketRocketIO/SockAddr.swift
Normal file
106
SocketRocketIO/SockAddr.swift
Normal 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(©) { 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(©) { 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
568
SocketRocketIO/Socket.swift
Normal 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
|
||||
// }
|
||||
//}
|
||||
18
SocketRocketIO/SocketRocketIO.h
Normal file
18
SocketRocketIO/SocketRocketIO.h
Normal 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
127
SocketRocketIO/Stream.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
104
SocketRocketIO/Unicode.swift
Normal file
104
SocketRocketIO/Unicode.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
131
SocketRocketIOTests/BufferedInputStreamTests.swift
Normal file
131
SocketRocketIOTests/BufferedInputStreamTests.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
5
SocketRocketIOTests/Fixtures/FileToReadFrom.txt
Normal file
5
SocketRocketIOTests/Fixtures/FileToReadFrom.txt
Normal file
@ -0,0 +1,5 @@
|
||||
hello
|
||||
this
|
||||
is a file
|
||||
|
||||
I like to read from it
|
||||
212
SocketRocketIOTests/Fixtures/UTF8Sample.txt
Normal file
212
SocketRocketIOTests/Fixtures/UTF8Sample.txt
Normal 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:
|
||||
|
||||
((V⍳V)=⍳⍴V)/V←,V ⌷←⍳→⍴∆∇⊃‾⍎⍕⌈
|
||||
|
||||
Nicer typography in plain text files:
|
||||
|
||||
╔══════════════════════════════════════════╗
|
||||
║ ║
|
||||
║ • ‘single’ and “double” quotes ║
|
||||
║ ║
|
||||
║ • Curly apostrophes: “We’ve been here” ║
|
||||
║ ║
|
||||
║ • Latin-1 apostrophe and accents: '´` ║
|
||||
║ ║
|
||||
║ • ‚deutsche‘ „Anführungszeichen“ ║
|
||||
║ ║
|
||||
║ • †, ‡, ‰, •, 3–4, —, −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: █
|
||||
▉
|
||||
╔══╦══╗ ┌──┬──┐ ╭──┬──╮ ╭──┬──╮ ┏━━┳━━┓ ┎┒┏┑ ╷ ╻ ┏┯┓ ┌┰┐ ▊ ╱╲╱╲╳╳╳
|
||||
║┌─╨─┐║ │╔═╧═╗│ │╒═╪═╕│ │╓─╁─╖│ ┃┌─╂─┐┃ ┗╃╄┙ ╶┼╴╺╋╸┠┼┨ ┝╋┥ ▋ ╲╱╲╱╳╳╳
|
||||
║│╲ ╱│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╿ │┃ ┍╅╆┓ ╵ ╹ ┗┷┛ └┸┘ ▌ ╱╲╱╲╳╳╳
|
||||
╠╡ ╳ ╞╣ ├╢ ╟┤ ├┼─┼─┼┤ ├╫─╂─╫┤ ┣┿╾┼╼┿┫ ┕┛┖┚ ┌┄┄┐ ╎ ┏┅┅┓ ┋ ▍ ╲╱╲╱╳╳╳
|
||||
║│╱ ╲│║ │║ ║│ ││ │ ││ │║ ┃ ║│ ┃│ ╽ │┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▎
|
||||
║└─╥─┘║ │╚═╤═╝│ │╘═╪═╛│ │╙─╀─╜│ ┃└─╂─┘┃ ░░▒▒▓▓██ ┊ ┆ ╎ ╏ ┇ ┋ ▏
|
||||
╚══╩══╝ └──┴──┘ ╰──┴──╯ ╰──┴──╯ ┗━━┻━━┛ ▗▄▖▛▀▜ └╌╌┘ ╎ ┗╍╍┛ ┋ ▁▂▃▄▅▆▇█
|
||||
▝▀▘▙▄▟
|
||||
18
SocketRocketIOTests/IOTests.swift
Normal file
18
SocketRocketIOTests/IOTests.swift
Normal 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 {
|
||||
}
|
||||
24
SocketRocketIOTests/Info.plist
Normal file
24
SocketRocketIOTests/Info.plist
Normal 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>
|
||||
145
SocketRocketIOTests/QueueStreamableTests.swift
Normal file
145
SocketRocketIOTests/QueueStreamableTests.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
213
SocketRocketIOTests/SocketTests.swift
Normal file
213
SocketRocketIOTests/SocketTests.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
171
SocketRocketIOTests/UnicodeTests.swift
Normal file
171
SocketRocketIOTests/UnicodeTests.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
108
SocketRocketIOTests/Util.swift
Normal file
108
SocketRocketIOTests/Util.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
@ -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>
|
||||
|
||||
24
SocketRocketTests/Info.plist
Normal file
24
SocketRocketTests/Info.plist
Normal 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>
|
||||
271
SocketRocketTests/WebSocketProtocolTests.swift
Normal file
271
SocketRocketTests/WebSocketProtocolTests.swift
Normal 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
26
SystemShims/Info.plist
Normal 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
45
SystemShims/SystemShims.h
Normal 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
24
SystemShims/SystemShims.m
Normal 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);
|
||||
}
|
||||
@ -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
130
TestChat/TCAppDelegate.mm
Normal 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
|
||||
@ -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>
|
||||
|
||||
96
TestHarness/test_harness.go
Normal file
96
TestHarness/test_harness.go
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user