add missing updates

This commit is contained in:
Erik 2017-07-02 11:32:56 -04:00
parent d1cbbc0213
commit c6dd1fb19b
No known key found for this signature in database
GPG Key ID: 4930B7C5FBC1A69D
5 changed files with 243 additions and 23 deletions

View File

@ -1,5 +1,9 @@
import PackageDescription
let package = Package(
name: "SocketIO"
name: "SocketIO",
dependencies: [
.Package(url: "https://github.com/daltoniam/zlib-spm.git", majorVersion: 1),
.Package(url: "https://github.com/daltoniam/common-crypto-spm.git", majorVersion: 1)
]
)

View File

@ -18,5 +18,6 @@ Pod::Spec.new do |s|
s.source_files = "Source/**/*.swift"
s.requires_arc = true
s.pod_target_xcconfig = {'SWIFT_VERSION' => '3.1'}
s.libraries = 'z'
# s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files
end

View File

@ -99,6 +99,16 @@
74BC45AB1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74BC45AC1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74BC45AD1D0C6675008CC431 /* SocketClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */; };
74DA216E1F0943EE009C19EE /* include.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DA216C1F09438D009C19EE /* include.h */; };
74DA216F1F0943F4009C19EE /* include.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DA216C1F09438D009C19EE /* include.h */; };
74DA21701F0943F8009C19EE /* include.h in Headers */ = {isa = PBXBuildFile; fileRef = 74DA216C1F09438D009C19EE /* include.h */; };
74DA21721F094408009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21711F094408009C19EE /* libz.tbd */; };
74DA21741F09440F009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21731F09440F009C19EE /* libz.tbd */; };
74DA21761F094417009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21751F094417009C19EE /* libz.tbd */; };
74DA217C1F09457B009C19EE /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 74DA21731F09440F009C19EE /* libz.tbd */; };
74DA21811F094887009C19EE /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74DA21801F094887009C19EE /* Compression.swift */; };
74DA21821F094887009C19EE /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74DA21801F094887009C19EE /* Compression.swift */; };
74DA21831F094887009C19EE /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74DA21801F094887009C19EE /* Compression.swift */; };
74F124F01BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
74F124F11BC574CF002966F4 /* SocketBasicPacketTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */; };
CEBA569A1CDA0B8200BA0389 /* SocketExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBA56991CDA0B8200BA0389 /* SocketExtensions.swift */; };
@ -175,6 +185,13 @@
749642B41D3FCE5500DD32D1 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WebSocket.swift; path = Source/WebSocket.swift; sourceTree = "<group>"; };
74ABF7761C3991C10078C657 /* SocketIOClientSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketIOClientSpec.swift; path = Source/SocketIOClientSpec.swift; sourceTree = "<group>"; };
74BC45AA1D0C6675008CC431 /* SocketClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketClientManager.swift; path = Source/SocketClientManager.swift; sourceTree = "<group>"; };
74DA216C1F09438D009C19EE /* include.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = include.h; path = zlib/include.h; sourceTree = "<group>"; };
74DA21711F094408009C19EE /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
74DA21731F09440F009C19EE /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
74DA21751F094417009C19EE /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.2.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
74DA21771F09444E009C19EE /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; name = module.modulemap; path = zlib/module.modulemap; sourceTree = "<group>"; };
74DA217D1F0945E9009C19EE /* libcommonCrypto.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libcommonCrypto.tbd; path = usr/lib/system/libcommonCrypto.tbd; sourceTree = SDKROOT; };
74DA21801F094887009C19EE /* Compression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Compression.swift; path = Source/Compression.swift; sourceTree = "<group>"; };
74F124EF1BC574CF002966F4 /* SocketBasicPacketTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketBasicPacketTest.swift; sourceTree = "<group>"; };
CEBA56991CDA0B8200BA0389 /* SocketExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SocketExtensions.swift; path = Source/SocketExtensions.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -184,6 +201,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
74DA21721F094408009C19EE /* libz.tbd in Frameworks */,
6CA08A961D615C040061FD2A /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -200,6 +218,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
74DA21741F09440F009C19EE /* libz.tbd in Frameworks */,
6CA08A981D615C0B0061FD2A /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -208,6 +227,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
74DA217C1F09457B009C19EE /* libz.tbd in Frameworks */,
572EF2431B51F18A00EEBB58 /* SocketIO.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -216,6 +236,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
74DA21761F094417009C19EE /* libz.tbd in Frameworks */,
6CA08A9A1D615C140061FD2A /* Security.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -234,6 +255,7 @@
572EF20D1B51F12F00EEBB58 = {
isa = PBXGroup;
children = (
74DA216B1F094371009C19EE /* zlib */,
6CA08A9B1D615C190061FD2A /* Frameworks */,
572EF21A1B51F16C00EEBB58 /* Products */,
572EF21B1B51F16C00EEBB58 /* SocketIO-iOS */,
@ -347,6 +369,10 @@
6CA08A9B1D615C190061FD2A /* Frameworks */ = {
isa = PBXGroup;
children = (
74DA217D1F0945E9009C19EE /* libcommonCrypto.tbd */,
74DA21751F094417009C19EE /* libz.tbd */,
74DA21731F09440F009C19EE /* libz.tbd */,
74DA21711F094408009C19EE /* libz.tbd */,
6CA08A9E1D615C340061FD2A /* tvOS */,
6CA08A9D1D615C2C0061FD2A /* Mac */,
6CA08A9C1D615C270061FD2A /* iOS */,
@ -381,12 +407,22 @@
74B4AD1B1D09A5C30062A523 /* Websocket */ = {
isa = PBXGroup;
children = (
74DA21801F094887009C19EE /* Compression.swift */,
749642B31D3FCE5500DD32D1 /* SSLSecurity.swift */,
749642B41D3FCE5500DD32D1 /* WebSocket.swift */,
);
name = Websocket;
sourceTree = "<group>";
};
74DA216B1F094371009C19EE /* zlib */ = {
isa = PBXGroup;
children = (
74DA216C1F09438D009C19EE /* include.h */,
74DA21771F09444E009C19EE /* module.modulemap */,
);
name = zlib;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@ -395,6 +431,7 @@
buildActionMask = 2147483647;
files = (
572EF21F1B51F16C00EEBB58 /* SocketIO.h in Headers */,
74DA21701F0943F8009C19EE /* include.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -403,6 +440,7 @@
buildActionMask = 2147483647;
files = (
572EF23D1B51F18A00EEBB58 /* SocketIO-Mac.h in Headers */,
74DA216F1F0943F4009C19EE /* include.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -411,6 +449,7 @@
buildActionMask = 2147483647;
files = (
57634A111BD9B46A00E19CD7 /* SocketIO.h in Headers */,
74DA216E1F0943EE009C19EE /* include.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -643,6 +682,7 @@
749642B51D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */,
74171EB71C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E811C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74DA21811F094887009C19EE /* Compression.swift in Sources */,
74171E6F1C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
747BC5991D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */,
74171E9F1C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
@ -685,6 +725,7 @@
749642B61D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */,
74171EB91C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E831C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74DA21821F094887009C19EE /* Compression.swift in Sources */,
74171E711C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
747BC59A1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */,
74171EA11C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
@ -731,6 +772,7 @@
749642B71D3FCE5500DD32D1 /* SSLSecurity.swift in Sources */,
74171EBB1C10CD240062D398 /* SocketParsable.swift in Sources */,
74171E851C10CD240062D398 /* SocketEnginePacketType.swift in Sources */,
74DA21831F094887009C19EE /* Compression.swift in Sources */,
74171E731C10CD240062D398 /* SocketAnyEvent.swift in Sources */,
747BC59B1D5F943500CA5FA4 /* SocketIOClientConfiguration.swift in Sources */,
74171EA31C10CD240062D398 /* SocketIOClientOption.swift in Sources */,
@ -885,6 +927,7 @@
PRODUCT_BUNDLE_IDENTIFIER = io.socket.SocketIOClientSwift;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_INCLUDE_PATHS = $SRCROOT/zlib;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -937,6 +980,7 @@
PRODUCT_BUNDLE_IDENTIFIER = io.socket.SocketIOClientSwift;
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_INCLUDE_PATHS = $SRCROOT/zlib;
SWIFT_VERSION = 3.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
@ -1088,11 +1132,16 @@
INFOPLIST_FILE = "SocketIO-Mac/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/system",
);
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_INCLUDE_PATHS = $SRCROOT/zlib;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 3.0;
VERSIONING_SYSTEM = "apple-generic";
@ -1142,10 +1191,15 @@
INFOPLIST_FILE = "SocketIO-Mac/Info.plist";
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/system",
);
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)";
SDKROOT = macosx;
SKIP_INSTALL = YES;
SWIFT_INCLUDE_PATHS = $SRCROOT/zlib;
SWIFT_VERSION = 3.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
@ -1196,6 +1250,10 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "SocketIO-MacTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/system",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
@ -1245,6 +1303,10 @@
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "SocketIO-MacTests/Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(SDKROOT)/usr/lib/system",
);
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = "io.socket.$(PRODUCT_NAME:rfc1034identifier)";
@ -1304,6 +1366,7 @@
PRODUCT_NAME = SocketIO;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_INCLUDE_PATHS = $SRCROOT/zlib;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
@ -1356,6 +1419,7 @@
PRODUCT_NAME = SocketIO;
SDKROOT = appletvos;
SKIP_INSTALL = YES;
SWIFT_INCLUDE_PATHS = $SRCROOT/zlib;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 9.0;
VALIDATE_PRODUCT = YES;

View File

@ -331,7 +331,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
}
ws?.callbackQueue = engineQueue
ws?.voipEnabled = voipEnabled
ws?.delegate = self
ws?.disableSSLCertValidation = selfSigned
ws?.security = security

View File

@ -20,7 +20,7 @@
//////////////////////////////////////////////////////////////////////////////////////////////////
import Foundation
import CoreFoundation
import Security
import CommonCrypto
public let WebsocketDidConnectNotification = "WebsocketDidConnectNotification"
public let WebsocketDidDisconnectNotification = "WebsocketDidDisconnectNotification"
@ -37,9 +37,19 @@ public protocol WebSocketPongDelegate: class {
func websocketDidReceivePong(socket: WebSocket, data: Data?)
}
// A Delegate with more advanced info on messages and connection etc.
public protocol WebSocketAdvancedDelegate: class {
func websocketDidConnect(socket: WebSocket)
func websocketDidDisconnect(socket: WebSocket, error: NSError?)
func websocketDidReceiveMessage(socket: WebSocket, text: String, response: WebSocket.WSResponse)
func websocketDidReceiveData(socket: WebSocket, data: Data, response: WebSocket.WSResponse)
func websocketHttpUpgrade(socket: WebSocket, request: CFHTTPMessage)
func websocketHttpUpgrade(socket: WebSocket, response: CFHTTPMessage)
}
open class WebSocket : NSObject, StreamDelegate {
enum OpCode : UInt8 {
public enum OpCode : UInt8 {
case continueFrame = 0x0
case textFrame = 0x1
case binaryFrame = 0x2
@ -68,6 +78,9 @@ open class WebSocket : NSObject, StreamDelegate {
enum InternalErrorCode: UInt16 {
// 0-999 WebSocket status codes not used
case outputStreamWriteError = 1
case compressionError = 2
case invalidSSLError = 3
case writeTimeoutError = 4
}
// Where the callback is executed. It defaults to the main UI thread queue.
@ -84,6 +97,7 @@ open class WebSocket : NSObject, StreamDelegate {
let headerWSProtocolName = "Sec-WebSocket-Protocol"
let headerWSVersionName = "Sec-WebSocket-Version"
let headerWSVersionValue = "13"
let headerWSExtensionName = "Sec-WebSocket-Extensions"
let headerWSKeyName = "Sec-WebSocket-Key"
let headerOriginName = "Origin"
let headerWSAcceptName = "Sec-WebSocket-Accept"
@ -91,18 +105,22 @@ open class WebSocket : NSObject, StreamDelegate {
let FinMask: UInt8 = 0x80
let OpCodeMask: UInt8 = 0x0F
let RSVMask: UInt8 = 0x70
let RSV1Mask: UInt8 = 0x40
let MaskMask: UInt8 = 0x80
let PayloadLenMask: UInt8 = 0x7F
let MaxFrameSize: Int = 32
let httpSwitchProtocolCode = 101
let supportedSSLSchemes = ["wss", "https"]
class WSResponse {
public class WSResponse {
var isFin = false
var code: OpCode = .continueFrame
public var code: OpCode = .continueFrame
var bytesLeft = 0
var frameCount = 0
var buffer: NSMutableData?
public var frameCount = 0
public var buffer: NSMutableData?
public let firstFrame = {
return Date()
}()
}
// MARK: - Delegates
@ -110,20 +128,46 @@ open class WebSocket : NSObject, StreamDelegate {
/// and also connection/disconnect messages.
public weak var delegate: WebSocketDelegate?
/// The optional advanced delegate can be used insteadof of the delegate
public weak var advancedDelegate: WebSocketAdvancedDelegate?
/// Receives a callback for each pong message recived.
public weak var pongDelegate: WebSocketPongDelegate?
// MARK: - Block based API.
public enum HTTPMethod {
case get
case post
case put
case connect
case custom(value: String)
var representation: String {
switch self {
case .get:
return "GET"
case .post:
return "POST"
case .put:
return "PUT"
case .connect:
return "CONNECT"
case .custom(let value):
return value.capitalized
}
}
}
public var onConnect: (() -> Void)?
public var onDisconnect: ((NSError?) -> Void)?
public var onText: ((String) -> Void)?
public var onData: ((Data) -> Void)?
public var onPong: ((Data?) -> Void)?
public var httpMethod: HTTPMethod = .get
public var headers = [String: String]()
public var voipEnabled = false
public var disableSSLCertValidation = false
public var enableCompression = true
public var security: SSLTrustValidator?
public var enabledSSLCipherSuites: [SSLCipherSuite]?
public var origin: String?
@ -135,11 +179,24 @@ open class WebSocket : NSObject, StreamDelegate {
public var currentURL: URL { return url }
// MARK: - Private
private struct CompressionState {
var supportsCompression = false
var messageNeedsDecompression = false
var serverMaxWindowBits = 15
var clientMaxWindowBits = 15
var clientNoContextTakeover = false
var serverNoContextTakeover = false
var decompressor:Decompressor? = nil
var compressor:Compressor? = nil
}
private var url: URL
private var inputStream: InputStream?
private var outputStream: OutputStream?
private var connected = false
private var isConnecting = false
private var compressionState = CompressionState()
private var writeQueue = OperationQueue()
private var readStack = [WSResponse]()
private var inputQueue = [Data]()
@ -147,6 +204,7 @@ open class WebSocket : NSObject, StreamDelegate {
private var certValidated = false
private var didDisconnect = false
private var readyToWrite = false
private var headerSecKey = ""
private let mutex = NSLock()
private let notificationCenter = NotificationCenter.default
private var canDispatch: Bool {
@ -246,7 +304,7 @@ open class WebSocket : NSObject, StreamDelegate {
Private method that starts the connection.
*/
private func createHTTPRequest() {
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, "GET" as CFString,
let urlRequest = CFHTTPMessageCreateRequest(kCFAllocatorDefault, httpMethod.representation as CFString,
url as CFURL, kCFHTTPVersion1_1).takeRetainedValue()
var port = url.port
@ -262,11 +320,16 @@ open class WebSocket : NSObject, StreamDelegate {
if let protocols = optionalProtocols {
addHeader(urlRequest, key: headerWSProtocolName, val: protocols.joined(separator: ","))
}
headerSecKey = generateWebSocketKey()
addHeader(urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
addHeader(urlRequest, key: headerWSKeyName, val: generateWebSocketKey())
addHeader(urlRequest, key: headerWSKeyName, val: headerSecKey)
if let origin = origin {
addHeader(urlRequest, key: headerOriginName, val: origin)
}
if enableCompression {
let val = "permessage-deflate; client_max_window_bits; server_max_window_bits=15"
addHeader(urlRequest, key: headerWSExtensionName, val: val)
}
addHeader(urlRequest, key: headerWSHostName, val: "\(url.host!):\(port!)")
for (key, value) in headers {
addHeader(urlRequest, key: key, val: value)
@ -274,6 +337,7 @@ open class WebSocket : NSObject, StreamDelegate {
if let cfHTTPMessage = CFHTTPMessageCopySerializedMessage(urlRequest) {
let serializedRequest = cfHTTPMessage.takeRetainedValue()
initStreamsWithData(serializedRequest as Data, Int(port!))
self.advancedDelegate?.websocketHttpUpgrade(socket: self, request: urlRequest)
}
}
@ -346,10 +410,6 @@ open class WebSocket : NSObject, StreamDelegate {
} else {
certValidated = true //not a https session, so no need to check SSL pinning
}
if voipEnabled {
inStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
outStream.setProperty(StreamNetworkServiceTypeValue.voIP as AnyObject, forKey: Stream.PropertyKey.networkServiceType)
}
CFReadStreamSetDispatchQueue(inStream, WebSocket.sharedWorkQueue)
CFWriteStreamSetDispatchQueue(outStream, WebSocket.sharedWorkQueue)
@ -373,7 +433,8 @@ open class WebSocket : NSObject, StreamDelegate {
WebSocket.sharedWorkQueue.async {
self?.cleanupStream()
}
self?.doDisconnect(self?.errorWithDetail("write wait timed out", code: 2))
let errCode = InternalErrorCode.writeTimeoutError.rawValue
self?.doDisconnect(self?.errorWithDetail("write wait timed out", code: errCode))
return
} else if outStream.streamError != nil {
return // disconnectStream will be called.
@ -382,12 +443,16 @@ open class WebSocket : NSObject, StreamDelegate {
guard !sOperation.isCancelled, let s = self else { return }
// Do the pinning now if needed
if let sec = s.security, !s.certValidated {
let trust = outStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust
let domain = outStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
s.certValidated = sec.isValid(trust, domain: domain)
if let possibleTrust = outStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) {
let domain = outStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as? String
s.certValidated = sec.isValid(possibleTrust as! SecTrust, domain: domain)
} else {
s.certValidated = false
}
if !s.certValidated {
WebSocket.sharedWorkQueue.async {
let error = s.errorWithDetail("Invalid SSL certificate", code: 1)
let errCode = InternalErrorCode.invalidSSLError.rawValue
let error = s.errorWithDetail("Invalid SSL certificate", code: errCode)
s.disconnectStream(error)
}
return
@ -539,6 +604,7 @@ open class WebSocket : NSObject, StreamDelegate {
guard let s = self else { return }
s.onConnect?()
s.delegate?.websocketDidConnect(socket: s)
s.advancedDelegate?.websocketDidConnect(socket: s)
s.notificationCenter.post(name: NSNotification.Name(WebsocketDidConnectNotification), object: self)
}
}
@ -559,13 +625,24 @@ open class WebSocket : NSObject, StreamDelegate {
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
CFHTTPMessageAppendBytes(response, buffer, bufferLen)
let code = CFHTTPMessageGetResponseStatusCode(response)
self.advancedDelegate?.websocketHttpUpgrade(socket: self, response: response)
if code != httpSwitchProtocolCode {
return code
}
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
let headers = cfHeaders.takeRetainedValue() as NSDictionary
if let extensionHeader = headers[headerWSExtensionName as NSString] as? String {
processExtensionHeader(extensionHeader)
}
if let acceptKey = headers[headerWSAcceptName as NSString] as? NSString {
if acceptKey.length > 0 {
if headerSecKey.characters.count > 0 {
let sha = "\(headerSecKey)258EAFA5-E914-47DA-95CA-C5AB0DC85B11".sha1Base64()
if sha != acceptKey as String {
return -1
}
}
return 0
}
}
@ -573,6 +650,37 @@ open class WebSocket : NSObject, StreamDelegate {
return -1
}
/**
Parses the extension header, setting up the compression parameters.
*/
func processExtensionHeader(_ extensionHeader: String) {
let parts = extensionHeader.components(separatedBy: ";")
for p in parts {
let part = p.trimmingCharacters(in: .whitespaces)
if part == "permessage-deflate" {
compressionState.supportsCompression = true
} else if part.hasPrefix("server_max_window_bits=") {
let valString = part.components(separatedBy: "=")[1]
if let val = Int(valString.trimmingCharacters(in: .whitespaces)) {
compressionState.serverMaxWindowBits = val
}
} else if part.hasPrefix("client_max_window_bits=") {
let valString = part.components(separatedBy: "=")[1]
if let val = Int(valString.trimmingCharacters(in: .whitespaces)) {
compressionState.clientMaxWindowBits = val
}
} else if part == "client_no_context_takeover" {
compressionState.clientNoContextTakeover = true
} else if part == "server_no_context_takeover" {
compressionState.serverNoContextTakeover = true
}
}
if compressionState.supportsCompression {
compressionState.decompressor = Decompressor(windowBits: compressionState.serverMaxWindowBits)
compressionState.compressor = Compressor(windowBits: compressionState.clientMaxWindowBits)
}
}
/**
Read a 16 bit big endian value from a buffer
*/
@ -637,7 +745,10 @@ open class WebSocket : NSObject, StreamDelegate {
let isMasked = (MaskMask & baseAddress[1])
let payloadLen = (PayloadLenMask & baseAddress[1])
var offset = 2
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .pong {
if compressionState.supportsCompression && receivedOpcode != .continueFrame {
compressionState.messageNeedsDecompression = (RSV1Mask & baseAddress[0]) > 0
}
if (isMasked > 0 || (RSVMask & baseAddress[0]) > 0) && receivedOpcode != .pong && !compressionState.messageNeedsDecompression {
let errCode = CloseCode.protocolError.rawValue
doDisconnect(errorWithDetail("masked and rsv data is not currently supported", code: errCode))
writeError(errCode)
@ -697,7 +808,23 @@ open class WebSocket : NSObject, StreamDelegate {
offset += size
len -= UInt64(size)
}
let data = Data(bytes: baseAddress+offset, count: Int(len))
let data: Data
if compressionState.messageNeedsDecompression, let decompressor = compressionState.decompressor {
do {
data = try decompressor.decompress(bytes: baseAddress+offset, count: Int(len), finish: isFin > 0)
if isFin > 0 && compressionState.serverNoContextTakeover{
try decompressor.reset()
}
} catch {
let closeReason = "Decompression failed: \(error)"
let closeCode = CloseCode.encoding.rawValue
doDisconnect(errorWithDetail(closeReason, code: closeCode))
writeError(closeCode)
return emptyBuffer
}
} else {
data = Data(bytes: baseAddress+offset, count: Int(len))
}
if receivedOpcode == .connectionClose {
var closeReason = "connection closed by server"
@ -804,6 +931,7 @@ open class WebSocket : NSObject, StreamDelegate {
guard let s = self else { return }
s.onText?(str! as String)
s.delegate?.websocketDidReceiveMessage(socket: s, text: str! as String)
s.advancedDelegate?.websocketDidReceiveMessage(socket: s, text: str! as String, response: response)
}
}
} else if response.code == .binaryFrame {
@ -813,6 +941,7 @@ open class WebSocket : NSObject, StreamDelegate {
guard let s = self else { return }
s.onData?(data as Data)
s.delegate?.websocketDidReceiveData(socket: s, data: data as Data)
s.advancedDelegate?.websocketDidReceiveData(socket: s, data: data as Data, response: response)
}
}
}
@ -851,10 +980,23 @@ open class WebSocket : NSObject, StreamDelegate {
guard let s = self else { return }
guard let sOperation = operation else { return }
var offset = 2
var firstByte:UInt8 = s.FinMask | code.rawValue
var data = data
if [.textFrame, .binaryFrame].contains(code), let compressor = s.compressionState.compressor {
do {
data = try compressor.compress(data)
if s.compressionState.clientNoContextTakeover {
try compressor.reset()
}
firstByte |= s.RSV1Mask
} catch {
// TODO: report error? We can just send the uncompressed frame.
}
}
let dataLength = data.count
let frame = NSMutableData(capacity: dataLength + s.MaxFrameSize)
let buffer = UnsafeMutableRawPointer(frame!.mutableBytes).assumingMemoryBound(to: UInt8.self)
buffer[0] = s.FinMask | code.rawValue
buffer[0] = firstByte
if dataLength < 126 {
buffer[1] = CUnsignedChar(dataLength)
} else if dataLength <= Int(UInt16.max) {
@ -920,6 +1062,7 @@ open class WebSocket : NSObject, StreamDelegate {
guard let s = self else { return }
s.onDisconnect?(error)
s.delegate?.websocketDidDisconnect(socket: s, error: error)
s.advancedDelegate?.websocketDidDisconnect(socket: s, error: error)
let userInfo = error.map{ [WebsocketDisconnectionErrorKeyName: $0] }
s.notificationCenter.post(name: NSNotification.Name(WebsocketDidDisconnectNotification), object: self, userInfo: userInfo)
}
@ -936,6 +1079,15 @@ open class WebSocket : NSObject, StreamDelegate {
}
private extension String {
func sha1Base64() -> String {
let data = self.data(using: String.Encoding.utf8)!
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
data.withUnsafeBytes { _ = CC_SHA1($0, CC_LONG(data.count), &digest) }
return Data(bytes: digest).base64EncodedString()
}
}
private extension Data {
init(buffer: UnsafeBufferPointer<UInt8>) {