merge master

This commit is contained in:
Erik 2015-10-01 13:01:13 -04:00
commit 927ab155e3
7 changed files with 88 additions and 55 deletions

View File

@ -64,7 +64,7 @@ Carthage
----------------- -----------------
Add this line to your `Cartfile`: Add this line to your `Cartfile`:
``` ```
github "socketio/socket.io-client-swift" ~> 3.1.2 # Or latest version github "socketio/socket.io-client-swift" ~> 3.1.4 # Or latest version
``` ```
Run `carthage update --platform ios,macosx`. Run `carthage update --platform ios,macosx`.
@ -83,7 +83,7 @@ source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0' platform :ios, '8.0'
use_frameworks! use_frameworks!
pod 'Socket.IO-Client-Swift', '~> 3.1.2' # Or latest version pod 'Socket.IO-Client-Swift', '~> 3.1.4' # Or latest version
``` ```
Install pods: Install pods:

View File

@ -1,6 +1,6 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "Socket.IO-Client-Swift" s.name = "Socket.IO-Client-Swift"
s.version = "3.1.2" s.version = "3.1.4"
s.summary = "Socket.IO-client for iOS and OS X" s.summary = "Socket.IO-client for iOS and OS X"
s.description = <<-DESC s.description = <<-DESC
Socket.IO-client for iOS and OS X. Socket.IO-client for iOS and OS X.
@ -12,7 +12,7 @@ Pod::Spec.new do |s|
s.author = { "Erik" => "nuclear.ace@gmail.com" } s.author = { "Erik" => "nuclear.ace@gmail.com" }
s.ios.deployment_target = '8.0' s.ios.deployment_target = '8.0'
s.osx.deployment_target = '10.10' s.osx.deployment_target = '10.10'
s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v3.1.2' } s.source = { :git => "https://github.com/socketio/socket.io-client-swift.git", :tag => 'v3.1.4' }
s.source_files = "SocketIOClientSwift/**/*.swift" s.source_files = "SocketIOClientSwift/**/*.swift"
s.requires_arc = true s.requires_arc = true
# s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files # s.dependency 'Starscream', '~> 0.9' # currently this repo includes Starscream swift files

View File

@ -19,7 +19,9 @@ class SocketParserTest: XCTestCase {
"61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", [ [1, 2], ["test": "bob"], 25, "polo", "~~0"], [], 19), "61-/swift,19[[1,2],{\"test\":\"bob\"},25,\"polo\",{\"_placeholder\":true,\"num\":0}]": ("/swift", [ [1, 2], ["test": "bob"], 25, "polo", "~~0"], [], 19),
"4/swift,": ("/swift", [], [], -1), "4/swift,": ("/swift", [], [], -1),
"0/swift": ("/swift", [], [], -1), "0/swift": ("/swift", [], [], -1),
"1/swift": ("/swift", [], [], -1)] "1/swift": ("/swift", [], [], -1),
"4\"ERROR\"": ("/", ["ERROR"], [], -1),
"41": ("/", [1], [], -1)]
func testDisconnect() { func testDisconnect() {
let message = "1" let message = "1"
@ -66,6 +68,16 @@ class SocketParserTest: XCTestCase {
validateParseResult(message) validateParseResult(message)
} }
func testErrorTypeString() {
let message = "4\"ERROR\""
validateParseResult(message)
}
func testErrorTypeInt() {
let message = "41"
validateParseResult(message)
}
func testInvalidInput() { func testInvalidInput() {
let message = "8" let message = "8"
switch SocketParser.parseString(message) { switch SocketParser.parseString(message) {

View File

@ -29,10 +29,10 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
private typealias ProbeWaitQueue = [Probe] private typealias ProbeWaitQueue = [Probe]
private let allowedCharacterSet = NSCharacterSet(charactersInString: "!*'();:@&=+$,/?%#[]\" {}").invertedSet private let allowedCharacterSet = NSCharacterSet(charactersInString: "!*'();:@&=+$,/?%#[]\" {}").invertedSet
private let emitQueue = dispatch_queue_create("engineEmitQueue", DISPATCH_QUEUE_SERIAL) private let emitQueue = dispatch_queue_create("com.socketio.engineEmitQueue", DISPATCH_QUEUE_SERIAL)
private let handleQueue = dispatch_queue_create("engineHandleQueue", DISPATCH_QUEUE_SERIAL) private let handleQueue = dispatch_queue_create("com.socketio.engineHandleQueue", DISPATCH_QUEUE_SERIAL)
private let logType = "SocketEngine" private let logType = "SocketEngine"
private let parseQueue = dispatch_queue_create("engineParseQueue", DISPATCH_QUEUE_SERIAL) private let parseQueue = dispatch_queue_create("com.socketio.engineParseQueue", DISPATCH_QUEUE_SERIAL)
private let session: NSURLSession! private let session: NSURLSession!
private let workQueue = NSOperationQueue() private let workQueue = NSOperationQueue()
@ -41,6 +41,7 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
private var fastUpgrade = false private var fastUpgrade = false
private var forcePolling = false private var forcePolling = false
private var forceWebsockets = false private var forceWebsockets = false
private var invalidated = false
private var pingInterval: Double? private var pingInterval: Double?
private var pingTimer: NSTimer? private var pingTimer: NSTimer?
private var pingTimeout = 0.0 { private var pingTimeout = 0.0 {
@ -69,7 +70,7 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
var urlWebSocket = "" var urlWebSocket = ""
var ws: WebSocket? var ws: WebSocket?
@objc public enum PacketType: Int { @objc public enum PacketType: Int {
case Open, Close, Ping, Pong, Message, Upgrade, Noop case Open, Close, Ping, Pong, Message, Upgrade, Noop
@ -99,14 +100,16 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
deinit { deinit {
Logger.log("Engine is being deinit", type: logType) Logger.log("Engine is being deinit", type: logType)
closed = true
stopPolling()
} }
private func checkIfMessageIsBase64Binary(var message: String) { private func checkIfMessageIsBase64Binary(var message: String) {
if message.hasPrefix("b4") { if message.hasPrefix("b4") {
// binary in base64 string // binary in base64 string
message.removeRange(Range<String.Index>(start: message.startIndex, message.removeRange(Range<String.Index>(start: message.startIndex,
end: message.startIndex.advancedBy(2))) end: message.startIndex.advancedBy(2)))
if let data = NSData(base64EncodedString: message, if let data = NSData(base64EncodedString: message,
options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters) { options: NSDataBase64DecodingOptions.IgnoreUnknownCharacters) {
client?.parseBinaryData(data) client?.parseBinaryData(data)
@ -147,7 +150,7 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
return .Right(str) return .Right(str)
} }
} }
private func createURLs(params: [String: AnyObject]?) -> (String, String) { private func createURLs(params: [String: AnyObject]?) -> (String, String) {
if client == nil { if client == nil {
return ("", "") return ("", "")
@ -190,22 +193,22 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
private func createWebsocketAndConnect(connect: Bool) { private func createWebsocketAndConnect(connect: Bool) {
let wsUrl = urlWebSocket + (sid == "" ? "" : "&sid=\(sid)") let wsUrl = urlWebSocket + (sid == "" ? "" : "&sid=\(sid)")
ws = WebSocket(url: NSURL(string: wsUrl)!) ws = WebSocket(url: NSURL(string: wsUrl)!)
if cookies != nil { if cookies != nil {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!) let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
for (key, value) in headers { for (key, value) in headers {
ws?.headers[key] = value ws?.headers[key] = value
} }
} }
if extraHeaders != nil { if extraHeaders != nil {
for (headerName, value) in extraHeaders! { for (headerName, value) in extraHeaders! {
ws?.headers[headerName] = value ws?.headers[headerName] = value
} }
} }
ws?.queue = handleQueue ws?.queue = handleQueue
ws?.delegate = self ws?.delegate = self
@ -240,26 +243,30 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!) let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
req.allHTTPHeaderFields = headers req.allHTTPHeaderFields = headers
} }
if extraHeaders != nil { if extraHeaders != nil {
for (headerName, value) in extraHeaders! { for (headerName, value) in extraHeaders! {
req.setValue(value, forHTTPHeaderField: headerName) req.setValue(value, forHTTPHeaderField: headerName)
} }
} }
doRequest(req) doLongPoll(req)
} }
private func doRequest(req: NSMutableURLRequest) { private func doRequest(req: NSMutableURLRequest,
if !polling || closed { withCallback callback: (NSData?, NSURLResponse?, NSError?) -> Void) {
return if !polling || closed || invalidated {
} return
}
req.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData Logger.log("Doing polling request", type: logType)
Logger.log("Doing polling request", type: logType) req.cachePolicy = .ReloadIgnoringLocalAndRemoteCacheData
session.dataTaskWithRequest(req, completionHandler: callback).resume()
}
session.dataTaskWithRequest(req) {[weak self] data, res, err in private func doLongPoll(req: NSMutableURLRequest) {
doRequest(req) {[weak self] data, res, err in
if let this = self { if let this = self {
if err != nil || data == nil { if err != nil || data == nil {
if this.polling { if this.polling {
@ -285,7 +292,8 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
} else if !this.closed && this.polling { } else if !this.closed && this.polling {
this.doPoll() this.doPoll()
} }
}}.resume() }
}
} }
private func flushProbeWait() { private func flushProbeWait() {
@ -344,13 +352,13 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
Logger.log("POSTing: %@", type: logType, args: postStr) Logger.log("POSTing: %@", type: logType, args: postStr)
session.dataTaskWithRequest(req) {[weak self] data, res, err in doRequest(req) {[weak self] data, res, err in
if let this = self { if let this = self {
if err != nil && this.polling { if err != nil && this.polling {
this.handlePollingFailed(err?.localizedDescription ?? "Error") this.handlePollingFailed(err?.localizedDescription ?? "Error")
return return
} else if err != nil { } else if err != nil {
NSLog(err?.localizedDescription ?? "Error") Logger.error(err?.localizedDescription ?? "Error", type: this.logType)
return return
} }
@ -362,14 +370,15 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
this?.doPoll() this?.doPoll()
} }
} }
}}.resume() }
}
} }
// We had packets waiting for send when we upgraded // We had packets waiting for send when we upgraded
// Send them raw // Send them raw
private func flushWaitingForPostToWebSocket() { private func flushWaitingForPostToWebSocket() {
guard let ws = self.ws else {return} guard let ws = self.ws else {return}
for msg in postWait { for msg in postWait {
ws.writeString(msg) ws.writeString(msg)
} }
@ -401,18 +410,18 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
self.sid = sid self.sid = sid
connected = true connected = true
if let upgrades = json?["upgrades"] as? [String] { if let upgrades = json?["upgrades"] as? [String] {
upgradeWs = upgrades.filter {$0 == "websocket"}.count != 0 upgradeWs = upgrades.filter {$0 == "websocket"}.count != 0
} else { } else {
upgradeWs = false upgradeWs = false
} }
if let pingInterval = json?["pingInterval"] as? Double, pingTimeout = json?["pingTimeout"] as? Double { if let pingInterval = json?["pingInterval"] as? Double, pingTimeout = json?["pingTimeout"] as? Double {
self.pingInterval = pingInterval / 1000.0 self.pingInterval = pingInterval / 1000.0
self.pingTimeout = pingTimeout / 1000.0 self.pingTimeout = pingTimeout / 1000.0
} }
if !forcePolling && !forceWebsockets && upgradeWs { if !forcePolling && !forceWebsockets && upgradeWs {
createWebsocketAndConnect(true) createWebsocketAndConnect(true)
} }
@ -456,7 +465,7 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
if connected { if connected {
Logger.error("Tried to open while connected", type: logType) Logger.error("Tried to open while connected", type: logType)
client?.didError("Tried to open while connected") client?.didError("Tried to open while connected")
return return
} }
@ -480,27 +489,27 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!) let headers = NSHTTPCookie.requestHeaderFieldsWithCookies(cookies!)
reqPolling.allHTTPHeaderFields = headers reqPolling.allHTTPHeaderFields = headers
} }
if let extraHeaders = extraHeaders { if let extraHeaders = extraHeaders {
for (headerName, value) in extraHeaders { for (headerName, value) in extraHeaders {
reqPolling.setValue(value, forHTTPHeaderField: headerName) reqPolling.setValue(value, forHTTPHeaderField: headerName)
} }
} }
doRequest(reqPolling) doLongPoll(reqPolling)
} }
private func parsePollingMessage(str: String) { private func parsePollingMessage(str: String) {
guard str.characters.count != 1 else { guard str.characters.count != 1 else {
return return
} }
var reader = SocketStringReader(message: str) var reader = SocketStringReader(message: str)
while reader.hasNext { while reader.hasNext {
if let n = Int(reader.readUntilStringOccurence(":")) { if let n = Int(reader.readUntilStringOccurence(":")) {
let str = reader.read(n) let str = reader.read(n)
dispatch_async(handleQueue) { dispatch_async(handleQueue) {
self.parseEngineMessage(str, fromPolling: true) self.parseEngineMessage(str, fromPolling: true)
} }
@ -626,6 +635,7 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
} }
func stopPolling() { func stopPolling() {
invalidated = true
session.finishTasksAndInvalidate() session.finishTasksAndInvalidate()
} }
@ -692,7 +702,7 @@ public final class SocketEngine: NSObject, WebSocketDelegate {
if error != nil { if error != nil {
client?.didError(reason) client?.didError(reason)
} }
client?.engineDidClose(reason) client?.engineDidClose(reason)
} else { } else {
flushProbeWait() flushProbeWait()

View File

@ -25,7 +25,7 @@
import Foundation import Foundation
public final class SocketIOClient: NSObject, SocketEngineClient { public final class SocketIOClient: NSObject, SocketEngineClient {
private let emitQueue = dispatch_queue_create("emitQueue", DISPATCH_QUEUE_SERIAL) private let emitQueue = dispatch_queue_create("com.socketio.emitQueue", DISPATCH_QUEUE_SERIAL)
private let handleQueue: dispatch_queue_t! private let handleQueue: dispatch_queue_t!
public let socketURL: String public let socketURL: String
@ -111,7 +111,6 @@ public final class SocketIOClient: NSObject, SocketEngineClient {
deinit { deinit {
Logger.log("Client is being deinit", type: logType) Logger.log("Client is being deinit", type: logType)
engine?.close(fast: true)
} }
private func addEngine() -> SocketEngine { private func addEngine() -> SocketEngine {
@ -172,10 +171,10 @@ public final class SocketIOClient: NSObject, SocketEngineClient {
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC)) let time = dispatch_time(DISPATCH_TIME_NOW, Int64(timeoutAfter) * Int64(NSEC_PER_SEC))
dispatch_after(time, dispatch_get_main_queue()) { dispatch_after(time, dispatch_get_main_queue()) {[weak self] in
if self.status != .Connected { if self != nil && self?.status != .Connected {
self.status = .Closed self?.status = .Closed
self.engine?.close(fast: true) self?.engine?.close(fast: true)
handler?() handler?()
} }

View File

@ -54,7 +54,7 @@ class SocketParser {
case .Disconnect: case .Disconnect:
socket.didDisconnect("Got Disconnect") socket.didDisconnect("Got Disconnect")
case .Error: case .Error:
socket.didError("Error: \(pack.data)") socket.didError(pack.data)
default: default:
Logger.log("Got invalid packet: %@", type: "SocketParser", args: pack.description) Logger.log("Got invalid packet: %@", type: "SocketParser", args: pack.description)
} }
@ -93,7 +93,11 @@ class SocketParser {
var idString = "" var idString = ""
while parser.hasNext { if type == .Error {
parser.advanceIndexBy(-1)
}
while parser.hasNext && type != .Error {
if let int = Int(parser.read(1)) { if let int = Int(parser.read(1)) {
idString += String(int) idString += String(int)
} else { } else {
@ -107,7 +111,13 @@ class SocketParser {
switch parseData(noPlaceholders) { switch parseData(noPlaceholders) {
case .Left(let err): case .Left(let err):
return .Left(err) // If first you don't succeed, try again
if case let .Right(data) = parseData("\([noPlaceholders as AnyObject])") {
return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1,
nsp: namespace ?? "/", placeholders: placeholders))
} else {
return .Left(err)
}
case .Right(let data): case .Right(let data):
return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1, return .Right(SocketPacket(type: type, data: data, id: Int(idString) ?? -1,
nsp: namespace ?? "/", placeholders: placeholders)) nsp: namespace ?? "/", placeholders: placeholders))

View File

@ -319,9 +319,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
let length = inputStream!.read(buffer, maxLength: BUFFER_MAX) let length = inputStream!.read(buffer, maxLength: BUFFER_MAX)
if length > 0 { if length > 0 {
if !connected { if !connected {
let status = processHTTP(buffer, bufferLen: length) connected = processHTTP(buffer, bufferLen: length)
if !status { if !connected {
doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: 1)) let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
CFHTTPMessageAppendBytes(response, buffer, length)
let code = CFHTTPMessageGetResponseStatusCode(response)
doDisconnect(errorWithDetail("Invalid HTTP upgrade", code: UInt16(code)))
} }
} else { } else {
var process = false var process = false
@ -372,7 +375,6 @@ public class WebSocket : NSObject, NSStreamDelegate {
if validateResponse(buffer, bufferLen: totalSize) { if validateResponse(buffer, bufferLen: totalSize) {
dispatch_async(queue,{ [weak self] in dispatch_async(queue,{ [weak self] in
guard let s = self else { return } guard let s = self else { return }
s.connected = true
if let connectBlock = s.onConnect { if let connectBlock = s.onConnect {
connectBlock() connectBlock()
} }