Merge branch 'development'
* development: Make timeOut(after:) Take a double for finer control of timeouts increase timeouts for travis document connect data item Add test for namespace in connect Tell users what namespace was connected to Clean up SocketPacket methods a bit Use guard
This commit is contained in:
commit
0013e7fdb6
@ -226,11 +226,11 @@ class SocketSideEffectTest: XCTestCase {
|
||||
|
||||
socket.setTestStatus(.notConnected)
|
||||
|
||||
socket.connect(timeoutAfter: 1, withHandler: {
|
||||
socket.connect(timeoutAfter: 0.5, withHandler: {
|
||||
expect.fulfill()
|
||||
})
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
waitForExpectations(timeout: 0.8)
|
||||
}
|
||||
|
||||
func testConnectDoesNotTimeOutIfConnected() {
|
||||
@ -238,22 +238,52 @@ class SocketSideEffectTest: XCTestCase {
|
||||
|
||||
socket.setTestStatus(.notConnected)
|
||||
|
||||
socket.connect(timeoutAfter: 1, withHandler: {
|
||||
socket.connect(timeoutAfter: 0.5, withHandler: {
|
||||
XCTFail("Should not call timeout handler if status is connected")
|
||||
})
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.2) {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||
// Fake connecting
|
||||
self.socket.setTestStatus(.connected)
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.1) {
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.7) {
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
}
|
||||
|
||||
func testConnectIsCalledWithNamepsace() {
|
||||
let expect = expectation(description: "The client should not call the timeout function")
|
||||
let nspString = "/swift"
|
||||
|
||||
socket.setTestStatus(.notConnected)
|
||||
|
||||
socket.on(clientEvent: .connect) {data, ack in
|
||||
guard let nsp = data[0] as? String else {
|
||||
XCTFail("Connect should be called with a namespace")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(nspString, nsp, "It should connect with the correct namespace")
|
||||
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
socket.connect(timeoutAfter: 0.3, withHandler: {
|
||||
XCTFail("Should not call timeout handler if status is connected")
|
||||
})
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 0.1) {
|
||||
// Fake connecting
|
||||
self.socket.parseEngineMessage("0/swift")
|
||||
}
|
||||
|
||||
waitForExpectations(timeout: 2)
|
||||
}
|
||||
|
||||
func testErrorInCustomSocketDataCallsErrorHandler() {
|
||||
let expect = expectation(description: "The client should call the error handler for emit errors because of " +
|
||||
"custom data")
|
||||
@ -289,7 +319,7 @@ class SocketSideEffectTest: XCTestCase {
|
||||
expect.fulfill()
|
||||
}
|
||||
|
||||
socket.emitWithAck("myEvent", ThrowingData()).timingOut(after: 1, callback: {_ in
|
||||
socket.emitWithAck("myEvent", ThrowingData()).timingOut(after: 0.8, callback: {_ in
|
||||
XCTFail("Ack callback should not be called")
|
||||
})
|
||||
|
||||
|
||||
@ -104,7 +104,7 @@ public final class OnAckCallback : NSObject {
|
||||
/// - parameter after: The number of seconds before this emit times out if an ack hasn't been received.
|
||||
/// - parameter callback: The callback called when an ack is received, or when a timeout happens.
|
||||
/// To check for timeout, use `SocketAckStatus`'s `noAck` case.
|
||||
public func timingOut(after seconds: Int, callback: @escaping AckCallback) {
|
||||
public func timingOut(after seconds: Double, callback: @escaping AckCallback) {
|
||||
guard let socket = self.socket, ackNumber != -1 else { return }
|
||||
|
||||
socket.ackHandlers.addAck(ackNumber, callback: callback)
|
||||
@ -112,7 +112,7 @@ public final class OnAckCallback : NSObject {
|
||||
|
||||
guard seconds != 0 else { return }
|
||||
|
||||
socket.handleQueue.asyncAfter(deadline: DispatchTime.now() + Double(seconds)) {
|
||||
socket.handleQueue.asyncAfter(deadline: DispatchTime.now() + seconds) {
|
||||
socket.ackHandlers.timeoutAck(self.ackNumber, onQueue: socket.handleQueue)
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,12 +203,12 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
||||
return OnAckCallback(ackNumber: currentAck, items: items, socket: self)
|
||||
}
|
||||
|
||||
func didConnect() {
|
||||
func didConnect(toNamespace namespace: String) {
|
||||
DefaultSocketLogger.Logger.log("Socket connected", type: logType)
|
||||
|
||||
status = .connected
|
||||
|
||||
handleClientEvent(.connect, data: [])
|
||||
handleClientEvent(.connect, data: [namespace])
|
||||
}
|
||||
|
||||
func didDisconnect(reason: String) {
|
||||
|
||||
@ -29,7 +29,7 @@ protocol SocketIOClientSpec : class {
|
||||
var nsp: String { get set }
|
||||
var waitingPackets: [SocketPacket] { get set }
|
||||
|
||||
func didConnect()
|
||||
func didConnect(toNamespace namespace: String)
|
||||
func didDisconnect(reason: String)
|
||||
func didError(reason: String)
|
||||
func handleAck(_ ack: Int, data: [Any])
|
||||
@ -48,7 +48,15 @@ extension SocketIOClientSpec {
|
||||
|
||||
/// The set of events that are generated by the client.
|
||||
public enum SocketClientEvent : String {
|
||||
/// Emitted when the client connects. This is also called on a successful reconnection.
|
||||
/// Emitted when the client connects. This is also called on a successful reconnection. A connect event gets one
|
||||
/// data item: the namespace that was connected to.
|
||||
///
|
||||
/// ```swift
|
||||
/// socket.on(clientEvent: .connect) {data, ack in
|
||||
/// guard let nsp = data[0] as? String else { return }
|
||||
/// // Some logic using the nsp
|
||||
/// }
|
||||
/// ```
|
||||
case connect
|
||||
|
||||
/// Called when the socket has disconnected and will not attempt to try to reconnect.
|
||||
|
||||
@ -29,15 +29,15 @@ struct SocketPacket {
|
||||
enum PacketType: Int {
|
||||
case connect, disconnect, event, ack, error, binaryEvent, binaryAck
|
||||
}
|
||||
|
||||
|
||||
private let placeholders: Int
|
||||
|
||||
|
||||
private static let logType = "SocketPacket"
|
||||
|
||||
let nsp: String
|
||||
let id: Int
|
||||
let type: PacketType
|
||||
|
||||
|
||||
var binary: [Data]
|
||||
var data: [Any]
|
||||
var args: [Any] {
|
||||
@ -47,20 +47,20 @@ struct SocketPacket {
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var description: String {
|
||||
return "SocketPacket {type: \(String(type.rawValue)); data: " +
|
||||
"\(String(describing: data)); id: \(id); placeholders: \(placeholders); nsp: \(nsp)}"
|
||||
}
|
||||
|
||||
|
||||
var event: String {
|
||||
return String(describing: data[0])
|
||||
}
|
||||
|
||||
|
||||
var packetString: String {
|
||||
return createPacketString()
|
||||
}
|
||||
|
||||
|
||||
init(type: PacketType, data: [Any] = [Any](), id: Int = -1, nsp: String, placeholders: Int = 0,
|
||||
binary: [Data] = [Data]()) {
|
||||
self.data = data
|
||||
@ -70,14 +70,14 @@ struct SocketPacket {
|
||||
self.placeholders = placeholders
|
||||
self.binary = binary
|
||||
}
|
||||
|
||||
|
||||
mutating func addData(_ data: Data) -> Bool {
|
||||
if placeholders == binary.count {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
binary.append(data)
|
||||
|
||||
|
||||
if placeholders == binary.count {
|
||||
fillInPlaceholders()
|
||||
return true
|
||||
@ -85,22 +85,19 @@ struct SocketPacket {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func completeMessage(_ message: String) -> String {
|
||||
if data.count == 0 {
|
||||
return message + "[]"
|
||||
}
|
||||
|
||||
guard data.count != 0 else { return message + "[]" }
|
||||
guard let jsonSend = try? data.toJSON(), let jsonString = String(data: jsonSend, encoding: .utf8) else {
|
||||
DefaultSocketLogger.Logger.error("Error creating JSON object in SocketPacket.completeMessage",
|
||||
type: SocketPacket.logType)
|
||||
|
||||
|
||||
return message + "[]"
|
||||
}
|
||||
|
||||
|
||||
return message + jsonString
|
||||
}
|
||||
|
||||
|
||||
private func createPacketString() -> String {
|
||||
let typeString = String(type.rawValue)
|
||||
// Binary count?
|
||||
@ -109,17 +106,17 @@ struct SocketPacket {
|
||||
let nspString = binaryCountString + (nsp != "/" ? "\(nsp)," : "")
|
||||
// Ack number?
|
||||
let idString = nspString + (id != -1 ? String(id) : "")
|
||||
|
||||
|
||||
return completeMessage(idString)
|
||||
}
|
||||
|
||||
|
||||
// Called when we have all the binary data for a packet
|
||||
// calls _fillInPlaceholders, which replaces placeholders with the
|
||||
// corresponding binary
|
||||
private mutating func fillInPlaceholders() {
|
||||
data = data.map(_fillInPlaceholders)
|
||||
}
|
||||
|
||||
|
||||
// Helper method that looks for placeholders
|
||||
// If object is a collection it will recurse
|
||||
// Returns the object if it is not a placeholder or the corresponding
|
||||
@ -132,9 +129,9 @@ struct SocketPacket {
|
||||
} else {
|
||||
return dict.reduce(JSON(), {cur, keyValue in
|
||||
var cur = cur
|
||||
|
||||
|
||||
cur[keyValue.0] = _fillInPlaceholders(keyValue.1)
|
||||
|
||||
|
||||
return cur
|
||||
})
|
||||
}
|
||||
@ -161,13 +158,12 @@ extension SocketPacket {
|
||||
return .error
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static func packetFromEmit(_ items: [Any], id: Int, nsp: String, ack: Bool) -> SocketPacket {
|
||||
let (parsedData, binary) = deconstructData(items)
|
||||
let packet = SocketPacket(type: findType(binary.count, ack: ack), data: parsedData,
|
||||
id: id, nsp: nsp, binary: binary)
|
||||
|
||||
return packet
|
||||
|
||||
return SocketPacket(type: findType(binary.count, ack: ack), data: parsedData, id: id, nsp: nsp,
|
||||
binary: binary)
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,32 +171,32 @@ private extension SocketPacket {
|
||||
// Recursive function that looks for NSData in collections
|
||||
static func shred(_ data: Any, binary: inout [Data]) -> Any {
|
||||
let placeholder = ["_placeholder": true, "num": binary.count] as JSON
|
||||
|
||||
|
||||
switch data {
|
||||
case let bin as Data:
|
||||
binary.append(bin)
|
||||
|
||||
|
||||
return placeholder
|
||||
case let arr as [Any]:
|
||||
return arr.map({shred($0, binary: &binary)})
|
||||
case let dict as JSON:
|
||||
return dict.reduce(JSON(), {cur, keyValue in
|
||||
var mutCur = cur
|
||||
|
||||
|
||||
mutCur[keyValue.0] = shred(keyValue.1, binary: &binary)
|
||||
|
||||
|
||||
return mutCur
|
||||
})
|
||||
default:
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Removes binary data from emit data
|
||||
// Returns a type containing the de-binaryed data and the binary
|
||||
static func deconstructData(_ data: [Any]) -> ([Any], [Data]) {
|
||||
var binary = [Data]()
|
||||
|
||||
return (data.map({shred($0, binary: &binary)}), binary)
|
||||
|
||||
return (data.map({ shred($0, binary: &binary) }), binary)
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,15 +31,15 @@ extension SocketParsable where Self: SocketIOClientSpec {
|
||||
private func isCorrectNamespace(_ nsp: String) -> Bool {
|
||||
return nsp == self.nsp
|
||||
}
|
||||
|
||||
|
||||
private func handleConnect(_ packetNamespace: String) {
|
||||
if packetNamespace == "/" && nsp != "/" {
|
||||
joinNamespace(nsp)
|
||||
} else {
|
||||
didConnect()
|
||||
didConnect(toNamespace: packetNamespace)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func handlePacket(_ pack: SocketPacket) {
|
||||
switch pack.type {
|
||||
case .event where isCorrectNamespace(pack.nsp):
|
||||
@ -60,22 +60,22 @@ extension SocketParsable where Self: SocketIOClientSpec {
|
||||
DefaultSocketLogger.Logger.log("Got invalid packet: %@", type: "SocketParser", args: pack.description)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Parses a messsage from the engine. Returning either a string error or a complete SocketPacket
|
||||
func parseString(_ message: String) -> Either<String, SocketPacket> {
|
||||
var reader = SocketStringReader(message: message)
|
||||
|
||||
|
||||
guard let type = Int(reader.read(count: 1)).flatMap({ SocketPacket.PacketType(rawValue: $0) }) else {
|
||||
return .left("Invalid packet type")
|
||||
}
|
||||
|
||||
|
||||
if !reader.hasNext {
|
||||
return .right(SocketPacket(type: type, nsp: "/"))
|
||||
}
|
||||
|
||||
|
||||
var namespace = "/"
|
||||
var placeholders = -1
|
||||
|
||||
|
||||
if type == .binaryEvent || type == .binaryAck {
|
||||
if let holders = Int(reader.readUntilOccurence(of: "-")) {
|
||||
placeholders = holders
|
||||
@ -83,17 +83,17 @@ extension SocketParsable where Self: SocketIOClientSpec {
|
||||
return .left("Invalid packet")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if reader.currentCharacter == "/" {
|
||||
namespace = reader.readUntilOccurence(of: ",")
|
||||
namespace = reader.readUntilOccurence(of: ",")
|
||||
}
|
||||
|
||||
|
||||
if !reader.hasNext {
|
||||
return .right(SocketPacket(type: type, nsp: namespace, placeholders: placeholders))
|
||||
}
|
||||
|
||||
|
||||
var idString = ""
|
||||
|
||||
|
||||
if type == .error {
|
||||
reader.advance(by: -1)
|
||||
} else {
|
||||
@ -106,13 +106,13 @@ extension SocketParsable where Self: SocketIOClientSpec {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var dataArray = String(message.utf16[message.utf16.index(reader.currentIndex, offsetBy: 1)..<message.utf16.endIndex])!
|
||||
|
||||
|
||||
if type == .error && !dataArray.hasPrefix("[") && !dataArray.hasSuffix("]") {
|
||||
dataArray = "[" + dataArray + "]"
|
||||
}
|
||||
|
||||
|
||||
switch parseData(dataArray) {
|
||||
case let .left(err):
|
||||
return .left(err)
|
||||
@ -121,7 +121,7 @@ extension SocketParsable where Self: SocketIOClientSpec {
|
||||
nsp: namespace, placeholders: placeholders))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parses data for events
|
||||
private func parseData(_ data: String) -> Either<String, [Any]> {
|
||||
do {
|
||||
@ -130,13 +130,13 @@ extension SocketParsable where Self: SocketIOClientSpec {
|
||||
return .left("Error parsing data for packet")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Parses messages recieved
|
||||
func parseSocketMessage(_ message: String) {
|
||||
guard !message.isEmpty else { return }
|
||||
|
||||
|
||||
DefaultSocketLogger.Logger.log("Parsing %@", type: "SocketParser", args: message)
|
||||
|
||||
|
||||
switch parseString(message) {
|
||||
case let .left(err):
|
||||
DefaultSocketLogger.Logger.error("\(err): %@", type: "SocketParser", args: message)
|
||||
@ -145,18 +145,18 @@ extension SocketParsable where Self: SocketIOClientSpec {
|
||||
handlePacket(pack)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func parseBinaryData(_ data: Data) {
|
||||
guard !waitingPackets.isEmpty else {
|
||||
DefaultSocketLogger.Logger.error("Got data when not remaking packet", type: "SocketParser")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Should execute event?
|
||||
guard waitingPackets[waitingPackets.count - 1].addData(data) else { return }
|
||||
|
||||
|
||||
let packet = waitingPackets.removeLast()
|
||||
|
||||
|
||||
if packet.type != .binaryAck {
|
||||
handleEvent(packet.event, data: packet.args, isInternalMessage: false, withAck: packet.id)
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user