Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1faa4b925c | ||
|
|
c20c286864 | ||
|
|
239b30f2c8 | ||
|
|
ecf9336132 | ||
|
|
bcaf7ba62d | ||
|
|
53289dc092 | ||
|
|
d0420bc3c6 | ||
|
|
26af5a6296 | ||
|
|
4eccf87ecc | ||
|
|
b85e42bbf9 | ||
|
|
7120d89a17 | ||
|
|
e0d8f16666 | ||
|
|
95b3bfc802 | ||
|
|
afc5a561d0 |
@ -1,9 +1,15 @@
|
|||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
|
let deps: [Package.Dependency]
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
|
deps = [.Package(url: "https://github.com/nuclearace/Starscream", majorVersion: 8)]
|
||||||
|
#else
|
||||||
|
deps = [.Package(url: "https://github.com/vapor/engine", majorVersion: 2)]
|
||||||
|
#endif
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "SocketIO",
|
name: "SocketIO",
|
||||||
dependencies: [
|
dependencies: deps,
|
||||||
.Package(url: "https://github.com/nuclearace/Starscream", majorVersion: 8),
|
|
||||||
],
|
|
||||||
exclude: ["Source/Starscream"]
|
exclude: ["Source/Starscream"]
|
||||||
)
|
)
|
||||||
|
|||||||
@ -136,6 +136,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
|
/// Not so type safe way to create a SocketIOClient, meant for Objective-C compatiblity.
|
||||||
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
|
/// If using Swift it's recommended to use `init(socketURL: NSURL, options: Set<SocketIOClientOption>)`
|
||||||
///
|
///
|
||||||
@ -144,6 +145,7 @@ open class SocketIOClient : NSObject, SocketIOClientSpec, SocketEngineClient, So
|
|||||||
public convenience init(socketURL: NSURL, config: NSDictionary?) {
|
public convenience init(socketURL: NSURL, config: NSDictionary?) {
|
||||||
self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? [])
|
self.init(socketURL: socketURL as URL, config: config?.toSocketConfiguration() ?? [])
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
DefaultSocketLogger.Logger.log("Client is being released", type: SocketIOClient.logType)
|
DefaultSocketLogger.Logger.log("Client is being released", type: SocketIOClient.logType)
|
||||||
|
|||||||
@ -22,8 +22,11 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
import Dispatch
|
||||||
import Foundation
|
import Foundation
|
||||||
|
#if !os(Linux)
|
||||||
import StarscreamSocketIO
|
import StarscreamSocketIO
|
||||||
|
#endif
|
||||||
|
|
||||||
protocol ClientOption : CustomStringConvertible, Equatable {
|
protocol ClientOption : CustomStringConvertible, Equatable {
|
||||||
func getSocketIOOptionValue() -> Any
|
func getSocketIOOptionValue() -> Any
|
||||||
|
|||||||
@ -24,7 +24,11 @@
|
|||||||
|
|
||||||
import Dispatch
|
import Dispatch
|
||||||
import Foundation
|
import Foundation
|
||||||
|
#if !os(Linux)
|
||||||
import StarscreamSocketIO
|
import StarscreamSocketIO
|
||||||
|
#else
|
||||||
|
import WebSockets
|
||||||
|
#endif
|
||||||
|
|
||||||
/// The class that handles the engine.io protocol and transports.
|
/// The class that handles the engine.io protocol and transports.
|
||||||
/// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods.
|
/// See `SocketEnginePollable` and `SocketEngineWebsocket` for transport specific methods.
|
||||||
@ -60,6 +64,9 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
/// **Do not touch this directly**
|
/// **Do not touch this directly**
|
||||||
public var waitingForPost = false
|
public var waitingForPost = false
|
||||||
|
|
||||||
|
/// The WebSocket for this engine.
|
||||||
|
public var ws: WebSocket?
|
||||||
|
|
||||||
/// `true` if this engine is closed.
|
/// `true` if this engine is closed.
|
||||||
public private(set) var closed = false
|
public private(set) var closed = false
|
||||||
|
|
||||||
@ -95,6 +102,15 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
/// If `true`, the engine is currently seeing whether it can upgrade to WebSockets.
|
/// If `true`, the engine is currently seeing whether it can upgrade to WebSockets.
|
||||||
public private(set) var probing = false
|
public private(set) var probing = false
|
||||||
|
|
||||||
|
/// Whether or not this engine uses secure transports
|
||||||
|
public private(set) var secure = false
|
||||||
|
|
||||||
|
/// A custom security validator for Starscream. Useful for SSL pinning.
|
||||||
|
public private(set) var security: SSLSecurity?
|
||||||
|
|
||||||
|
/// Whether or not to allow self signed certificates.
|
||||||
|
public private(set) var selfSigned = false
|
||||||
|
|
||||||
/// The URLSession that will be used for polling.
|
/// The URLSession that will be used for polling.
|
||||||
public private(set) var session: URLSession?
|
public private(set) var session: URLSession?
|
||||||
|
|
||||||
@ -113,9 +129,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
/// If `true`, then the engine is currently in WebSockets mode.
|
/// If `true`, then the engine is currently in WebSockets mode.
|
||||||
public private(set) var websocket = false
|
public private(set) var websocket = false
|
||||||
|
|
||||||
/// The WebSocket for this engine.
|
|
||||||
public private(set) var ws: WebSocket?
|
|
||||||
|
|
||||||
/// The client for this engine.
|
/// The client for this engine.
|
||||||
public weak var client: SocketEngineClient?
|
public weak var client: SocketEngineClient?
|
||||||
|
|
||||||
@ -133,9 +146,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
private var pongsMissed = 0
|
private var pongsMissed = 0
|
||||||
private var pongsMissedMax = 0
|
private var pongsMissedMax = 0
|
||||||
private var probeWait = ProbeWaitQueue()
|
private var probeWait = ProbeWaitQueue()
|
||||||
private var secure = false
|
|
||||||
private var security: SSLSecurity?
|
|
||||||
private var selfSigned = false
|
|
||||||
|
|
||||||
// MARK: Initializers
|
// MARK: Initializers
|
||||||
|
|
||||||
@ -206,7 +216,7 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
|
|
||||||
private func checkAndHandleEngineError(_ msg: String) {
|
private func checkAndHandleEngineError(_ msg: String) {
|
||||||
do {
|
do {
|
||||||
let dict = try msg.toNSDictionary()
|
let dict = try msg.toDictionary()
|
||||||
guard let error = dict["message"] as? String else { return }
|
guard let error = dict["message"] as? String else { return }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -308,32 +318,6 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
return (urlPolling.url!, urlWebSocket.url!)
|
return (urlPolling.url!, urlWebSocket.url!)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createWebSocketAndConnect() {
|
|
||||||
ws?.delegate = nil // TODO this seems a bit defensive, is this really needed?
|
|
||||||
ws = WebSocket(url: urlWebSocketWithSid)
|
|
||||||
|
|
||||||
if cookies != nil {
|
|
||||||
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
|
|
||||||
for (key, value) in headers {
|
|
||||||
ws?.headers[key] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if extraHeaders != nil {
|
|
||||||
for (headerName, value) in extraHeaders! {
|
|
||||||
ws?.headers[headerName] = value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ws?.callbackQueue = engineQueue
|
|
||||||
ws?.enableCompression = compress
|
|
||||||
ws?.delegate = self
|
|
||||||
ws?.disableSSLCertValidation = selfSigned
|
|
||||||
ws?.security = security
|
|
||||||
|
|
||||||
ws?.connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Called when an error happens during execution. Causes a disconnection.
|
/// Called when an error happens during execution. Causes a disconnection.
|
||||||
public func didError(reason: String) {
|
public func didError(reason: String) {
|
||||||
DefaultSocketLogger.Logger.error("%@", type: SocketEngine.logType, args: reason)
|
DefaultSocketLogger.Logger.error("%@", type: SocketEngine.logType, args: reason)
|
||||||
@ -435,7 +419,7 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func handleOpen(openData: String) {
|
private func handleOpen(openData: String) {
|
||||||
guard let json = try? openData.toNSDictionary() else {
|
guard let json = try? openData.toDictionary() else {
|
||||||
didError(reason: "Error parsing open packet")
|
didError(reason: "Error parsing open packet")
|
||||||
|
|
||||||
return
|
return
|
||||||
@ -486,6 +470,44 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when a successful WebSocket connection is made.
|
||||||
|
public func handleWSConnect() {
|
||||||
|
if !forceWebsockets {
|
||||||
|
probing = true
|
||||||
|
probeWebSocket()
|
||||||
|
} else {
|
||||||
|
connected = true
|
||||||
|
probing = false
|
||||||
|
polling = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called when the WebSocket disconnects.
|
||||||
|
public func handleWSDisconnect(error: NSError?) {
|
||||||
|
probing = false
|
||||||
|
|
||||||
|
if closed {
|
||||||
|
client?.engineDidClose(reason: "Disconnect")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard websocket else {
|
||||||
|
flushProbeWait()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
connected = false
|
||||||
|
websocket = false
|
||||||
|
|
||||||
|
if let reason = error?.localizedDescription {
|
||||||
|
didError(reason: reason)
|
||||||
|
} else {
|
||||||
|
client?.engineDidClose(reason: "Socket Disconnected")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parses raw binary received from engine.io.
|
/// Parses raw binary received from engine.io.
|
||||||
///
|
///
|
||||||
/// - parameter data: The data to parse.
|
/// - parameter data: The data to parse.
|
||||||
@ -601,45 +623,19 @@ public final class SocketEngine : NSObject, URLSessionDelegate, SocketEnginePoll
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
// MARK: Starscream delegate conformance
|
// MARK: Starscream delegate conformance
|
||||||
|
|
||||||
/// Delegate method for connection.
|
/// Delegate method for connection.
|
||||||
public func websocketDidConnect(socket: WebSocket) {
|
public func websocketDidConnect(socket: WebSocket) {
|
||||||
if !forceWebsockets {
|
handleWSConnect()
|
||||||
probing = true
|
|
||||||
probeWebSocket()
|
|
||||||
} else {
|
|
||||||
connected = true
|
|
||||||
probing = false
|
|
||||||
polling = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Delegate method for disconnection.
|
/// Delegate method for disconnection.
|
||||||
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
public func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
|
||||||
probing = false
|
handleWSDisconnect(error: error)
|
||||||
|
|
||||||
if closed {
|
|
||||||
client?.engineDidClose(reason: "Disconnect")
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard websocket else {
|
|
||||||
flushProbeWait()
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
connected = false
|
|
||||||
websocket = false
|
|
||||||
|
|
||||||
if let reason = error?.localizedDescription {
|
|
||||||
didError(reason: reason)
|
|
||||||
} else {
|
|
||||||
client?.engineDidClose(reason: "Socket Disconnected")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SocketEngine {
|
extension SocketEngine {
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
/// Declares that a type will be a delegate to an engine.
|
/// Declares that a type will be a delegate to an engine.
|
||||||
@objc public protocol SocketEngineClient {
|
@objc public protocol SocketEngineClient {
|
||||||
// MARK: Methods
|
// MARK: Methods
|
||||||
@ -54,3 +55,34 @@ import Foundation
|
|||||||
/// - parameter data: The data the engine received.
|
/// - parameter data: The data the engine received.
|
||||||
func parseEngineBinaryData(_ data: Data)
|
func parseEngineBinaryData(_ data: Data)
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
/// Declares that a type will be a delegate to an engine.
|
||||||
|
public protocol SocketEngineClient : class {
|
||||||
|
// MARK: Methods
|
||||||
|
|
||||||
|
/// Called when the engine errors.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason the engine errored.
|
||||||
|
func engineDidError(reason: String)
|
||||||
|
|
||||||
|
/// Called when the engine closes.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason that the engine closed.
|
||||||
|
func engineDidClose(reason: String)
|
||||||
|
|
||||||
|
/// Called when the engine opens.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason the engine opened.
|
||||||
|
func engineDidOpen(reason: String)
|
||||||
|
|
||||||
|
/// Called when the engine has a message that must be parsed.
|
||||||
|
///
|
||||||
|
/// - parameter msg: The message that needs parsing.
|
||||||
|
func parseEngineMessage(_ msg: String)
|
||||||
|
|
||||||
|
/// Called when the engine receives binary data.
|
||||||
|
///
|
||||||
|
/// - parameter data: The data the engine received.
|
||||||
|
func parseEngineBinaryData(_ data: Data)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@ -23,9 +23,15 @@
|
|||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import Dispatch
|
||||||
import Foundation
|
import Foundation
|
||||||
|
#if !os(Linux)
|
||||||
import StarscreamSocketIO
|
import StarscreamSocketIO
|
||||||
|
#else
|
||||||
|
import WebSockets
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
/// Specifies a SocketEngine.
|
/// Specifies a SocketEngine.
|
||||||
@objc public protocol SocketEngineSpec {
|
@objc public protocol SocketEngineSpec {
|
||||||
/// The client for this engine.
|
/// The client for this engine.
|
||||||
@ -40,6 +46,9 @@ import StarscreamSocketIO
|
|||||||
/// The connect parameters sent during a connect.
|
/// The connect parameters sent during a connect.
|
||||||
var connectParams: [String: Any]? { get set }
|
var connectParams: [String: Any]? { get set }
|
||||||
|
|
||||||
|
/// Whether or not to use WebSocket compression.
|
||||||
|
var compress: Bool { get }
|
||||||
|
|
||||||
/// An array of HTTPCookies that are sent during the connection.
|
/// An array of HTTPCookies that are sent during the connection.
|
||||||
var cookies: [HTTPCookie]? { get }
|
var cookies: [HTTPCookie]? { get }
|
||||||
|
|
||||||
@ -64,6 +73,15 @@ import StarscreamSocketIO
|
|||||||
/// If `true`, the engine is currently seeing whether it can upgrade to WebSockets.
|
/// If `true`, the engine is currently seeing whether it can upgrade to WebSockets.
|
||||||
var probing: Bool { get }
|
var probing: Bool { get }
|
||||||
|
|
||||||
|
/// Whether or not this engine uses secure transports
|
||||||
|
var secure: Bool { get }
|
||||||
|
|
||||||
|
/// A custom security validator for Starscream. Useful for SSL pinning.
|
||||||
|
var security: SSLSecurity? { get }
|
||||||
|
|
||||||
|
/// Whether or not to allow self signed certificates.
|
||||||
|
var selfSigned: Bool { get }
|
||||||
|
|
||||||
/// The session id for this engine.
|
/// The session id for this engine.
|
||||||
var sid: String { get }
|
var sid: String { get }
|
||||||
|
|
||||||
@ -80,7 +98,7 @@ import StarscreamSocketIO
|
|||||||
var websocket: Bool { get }
|
var websocket: Bool { get }
|
||||||
|
|
||||||
/// The WebSocket for this engine.
|
/// The WebSocket for this engine.
|
||||||
var ws: WebSocket? { get }
|
var ws: WebSocket? { get set }
|
||||||
|
|
||||||
/// Creates a new engine.
|
/// Creates a new engine.
|
||||||
///
|
///
|
||||||
@ -131,6 +149,123 @@ import StarscreamSocketIO
|
|||||||
/// - parameter withData: Any data that this message has.
|
/// - parameter withData: Any data that this message has.
|
||||||
func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data])
|
func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data])
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
public protocol SocketEngineSpec : class {
|
||||||
|
/// The client for this engine.
|
||||||
|
var client: SocketEngineClient? { get set }
|
||||||
|
|
||||||
|
/// `true` if this engine is closed.
|
||||||
|
var closed: Bool { get }
|
||||||
|
|
||||||
|
/// `true` if this engine is connected. Connected means that the initial poll connect has succeeded.
|
||||||
|
var connected: Bool { get }
|
||||||
|
|
||||||
|
/// The connect parameters sent during a connect.
|
||||||
|
var connectParams: [String: Any]? { get set }
|
||||||
|
|
||||||
|
/// Whether or not to use WebSocket compression.
|
||||||
|
var compress: Bool { get }
|
||||||
|
|
||||||
|
/// An array of HTTPCookies that are sent during the connection.
|
||||||
|
var cookies: [HTTPCookie]? { get }
|
||||||
|
|
||||||
|
/// The queue that all engine actions take place on.
|
||||||
|
var engineQueue: DispatchQueue { get }
|
||||||
|
|
||||||
|
/// A dictionary of extra http headers that will be set during connection.
|
||||||
|
var extraHeaders: [String: String]? { get }
|
||||||
|
|
||||||
|
/// When `true`, the engine is in the process of switching to WebSockets.
|
||||||
|
var fastUpgrade: Bool { get }
|
||||||
|
|
||||||
|
/// When `true`, the engine will only use HTTP long-polling as a transport.
|
||||||
|
var forcePolling: Bool { get }
|
||||||
|
|
||||||
|
/// When `true`, the engine will only use WebSockets as a transport.
|
||||||
|
var forceWebsockets: Bool { get }
|
||||||
|
|
||||||
|
/// If `true`, the engine is currently in HTTP long-polling mode.
|
||||||
|
var polling: Bool { get }
|
||||||
|
|
||||||
|
/// If `true`, the engine is currently seeing whether it can upgrade to WebSockets.
|
||||||
|
var probing: Bool { get }
|
||||||
|
|
||||||
|
/// Whether or not this engine uses secure transports
|
||||||
|
var secure: Bool { get }
|
||||||
|
|
||||||
|
var security: SSLSecurity? { get }
|
||||||
|
|
||||||
|
/// Whether or not to allow self signed certificates.
|
||||||
|
var selfSigned: Bool { get }
|
||||||
|
|
||||||
|
/// The session id for this engine.
|
||||||
|
var sid: String { get }
|
||||||
|
|
||||||
|
/// The path to engine.io.
|
||||||
|
var socketPath: String { get }
|
||||||
|
|
||||||
|
/// The url for polling.
|
||||||
|
var urlPolling: URL { get }
|
||||||
|
|
||||||
|
/// The url for WebSockets.
|
||||||
|
var urlWebSocket: URL { get }
|
||||||
|
|
||||||
|
/// If `true`, then the engine is currently in WebSockets mode.
|
||||||
|
var websocket: Bool { get }
|
||||||
|
|
||||||
|
/// The WebSocket for this engine.
|
||||||
|
var ws: WebSocket? { get set }
|
||||||
|
|
||||||
|
/// Creates a new engine.
|
||||||
|
///
|
||||||
|
/// - parameter client: The client for this engine.
|
||||||
|
/// - parameter url: The url for this engine.
|
||||||
|
/// - parameter options: The options for this engine.
|
||||||
|
init(client: SocketEngineClient, url: URL, options: NSDictionary?)
|
||||||
|
|
||||||
|
/// Starts the connection to the server.
|
||||||
|
func connect()
|
||||||
|
|
||||||
|
/// Called when an error happens during execution. Causes a disconnection.
|
||||||
|
func didError(reason: String)
|
||||||
|
|
||||||
|
/// Disconnects from the server.
|
||||||
|
///
|
||||||
|
/// - parameter reason: The reason for the disconnection. This is communicated up to the client.
|
||||||
|
func disconnect(reason: String)
|
||||||
|
|
||||||
|
/// Called to switch from HTTP long-polling to WebSockets. After calling this method the engine will be in
|
||||||
|
/// WebSocket mode.
|
||||||
|
///
|
||||||
|
/// **You shouldn't call this directly**
|
||||||
|
func doFastUpgrade()
|
||||||
|
|
||||||
|
/// Causes any packets that were waiting for POSTing to be sent through the WebSocket. This happens because when
|
||||||
|
/// the engine is attempting to upgrade to WebSocket it does not do any POSTing.
|
||||||
|
///
|
||||||
|
/// **You shouldn't call this directly**
|
||||||
|
func flushWaitingForPostToWebSocket()
|
||||||
|
|
||||||
|
/// Parses raw binary received from engine.io.
|
||||||
|
///
|
||||||
|
/// - parameter data: The data to parse.
|
||||||
|
func parseEngineData(_ data: Data)
|
||||||
|
|
||||||
|
/// Parses a raw engine.io packet.
|
||||||
|
///
|
||||||
|
/// - parameter message: The message to parse.
|
||||||
|
/// - parameter fromPolling: Whether this message is from long-polling.
|
||||||
|
/// If `true` we might have to fix utf8 encoding.
|
||||||
|
func parseEngineMessage(_ message: String)
|
||||||
|
|
||||||
|
/// Writes a message to engine.io, independent of transport.
|
||||||
|
///
|
||||||
|
/// - parameter msg: The message to send.
|
||||||
|
/// - parameter withType: The type of this message.
|
||||||
|
/// - parameter withData: Any data that this message has.
|
||||||
|
func write(_ msg: String, withType type: SocketEnginePacketType, withData data: [Data])
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
extension SocketEngineSpec {
|
extension SocketEngineSpec {
|
||||||
var urlPollingWithSid: URL {
|
var urlPollingWithSid: URL {
|
||||||
@ -161,12 +296,11 @@ extension SocketEngineSpec {
|
|||||||
|
|
||||||
func createBinaryDataForSend(using data: Data) -> Either<Data, String> {
|
func createBinaryDataForSend(using data: Data) -> Either<Data, String> {
|
||||||
if websocket {
|
if websocket {
|
||||||
var byteArray = [UInt8](repeating: 0x4, count: 1)
|
var mutData = Data(bytes: [UInt8](repeating: 0x4, count: 1))
|
||||||
let mutData = NSMutableData(bytes: &byteArray, length: 1)
|
|
||||||
|
|
||||||
mutData.append(data)
|
mutData.append(data)
|
||||||
|
|
||||||
return .left(mutData as Data)
|
return .left(mutData)
|
||||||
} else {
|
} else {
|
||||||
return .right("b4" + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)))
|
return .right("b4" + data.base64EncodedString(options: Data.Base64EncodingOptions(rawValue: 0)))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,10 +24,22 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
#if !os(Linux)
|
||||||
import StarscreamSocketIO
|
import StarscreamSocketIO
|
||||||
|
#else
|
||||||
|
import WebSockets
|
||||||
|
import Sockets
|
||||||
|
import TLS
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Protocol that is used to implement socket.io WebSocket support
|
/// Protocol that is used to implement socket.io WebSocket support
|
||||||
public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate {
|
public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate {
|
||||||
|
/// Called when a successful WebSocket connection is made.
|
||||||
|
func handleWSConnect()
|
||||||
|
|
||||||
|
/// Called when the WebSocket disconnects.
|
||||||
|
func handleWSDisconnect(error: NSError?)
|
||||||
|
|
||||||
/// Sends an engine.io message through the WebSocket transport.
|
/// Sends an engine.io message through the WebSocket transport.
|
||||||
///
|
///
|
||||||
/// You shouldn't call this directly, instead call the `write` method on `SocketEngine`.
|
/// You shouldn't call this directly, instead call the `write` method on `SocketEngine`.
|
||||||
@ -40,6 +52,89 @@ public protocol SocketEngineWebsocket : SocketEngineSpec, WebSocketDelegate {
|
|||||||
|
|
||||||
// WebSocket methods
|
// WebSocket methods
|
||||||
extension SocketEngineWebsocket {
|
extension SocketEngineWebsocket {
|
||||||
|
#if os(Linux)
|
||||||
|
func attachWebSocketHandlers() {
|
||||||
|
ws?.onText = {[weak self] ws, text in
|
||||||
|
guard let this = self else { return }
|
||||||
|
|
||||||
|
this.parseEngineMessage(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
ws?.onBinary = {[weak self] ws, bin in
|
||||||
|
guard let this = self else { return }
|
||||||
|
|
||||||
|
this.parseEngineData(Data(bytes: bin))
|
||||||
|
}
|
||||||
|
|
||||||
|
ws?.onClose = {[weak self] _, _, reason, clean in
|
||||||
|
guard let this = self else { return }
|
||||||
|
|
||||||
|
this.handleWSDisconnect(error: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
func createWebSocketAndConnect() {
|
||||||
|
#if !os(Linux)
|
||||||
|
ws?.delegate = nil // TODO this seems a bit defensive, is this really needed?
|
||||||
|
ws = WebSocket(url: urlWebSocketWithSid)
|
||||||
|
|
||||||
|
if cookies != nil {
|
||||||
|
let headers = HTTPCookie.requestHeaderFields(with: cookies!)
|
||||||
|
for (key, value) in headers {
|
||||||
|
ws?.headers[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if extraHeaders != nil {
|
||||||
|
for (headerName, value) in extraHeaders! {
|
||||||
|
ws?.headers[headerName] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ws?.callbackQueue = engineQueue
|
||||||
|
ws?.enableCompression = compress
|
||||||
|
ws?.delegate = self
|
||||||
|
ws?.disableSSLCertValidation = selfSigned
|
||||||
|
ws?.security = security
|
||||||
|
|
||||||
|
ws?.connect()
|
||||||
|
#else
|
||||||
|
let url = urlWebSocketWithSid
|
||||||
|
do {
|
||||||
|
let socket = try TCPInternetSocket(scheme: url.scheme ?? "http",
|
||||||
|
hostname: url.host ?? "localhost",
|
||||||
|
port: Port(url.port ?? 80))
|
||||||
|
|
||||||
|
if secure {
|
||||||
|
let stream = try TLS.InternetSocket(socket, TLS.Context(.client))
|
||||||
|
try WebSocket.background(to: urlWebSocketWithSid.absoluteString, using: stream) {[weak self] ws in
|
||||||
|
guard let this = self else { return }
|
||||||
|
|
||||||
|
this.onConnect(ws: ws)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try WebSocket.background(to: urlWebSocketWithSid.absoluteString, using: socket) {[weak self] ws in
|
||||||
|
guard let this = self else { return }
|
||||||
|
|
||||||
|
this.onConnect(ws: ws)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
DefaultSocketLogger.Logger.error("Error connecting socket", type: "SocketEngineWebsocket")
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(Linux)
|
||||||
|
func onConnect(ws: WebSocket) {
|
||||||
|
self.ws = ws
|
||||||
|
|
||||||
|
attachWebSocketHandlers()
|
||||||
|
handleWSConnect()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
func probeWebSocket() {
|
func probeWebSocket() {
|
||||||
if ws?.isConnected ?? false {
|
if ws?.isConnected ?? false {
|
||||||
sendWebSocketMessage("probe", withType: .ping, withData: [])
|
sendWebSocketMessage("probe", withType: .ping, withData: [])
|
||||||
@ -65,6 +160,7 @@ extension SocketEngineWebsocket {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
// MARK: Starscream delegate methods
|
// MARK: Starscream delegate methods
|
||||||
|
|
||||||
/// Delegate method for when a message is received.
|
/// Delegate method for when a message is received.
|
||||||
@ -76,4 +172,45 @@ extension SocketEngineWebsocket {
|
|||||||
public func websocketDidReceiveData(socket: WebSocket, data: Data) {
|
public func websocketDidReceiveData(socket: WebSocket, data: Data) {
|
||||||
parseEngineData(data)
|
parseEngineData(data)
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if os(Linux)
|
||||||
|
/// Does nothing on Linux.
|
||||||
|
public protocol WebSocketDelegate { }
|
||||||
|
|
||||||
|
/// SSLSecurity does nothing on Linux.
|
||||||
|
public final class SSLSecurity { }
|
||||||
|
|
||||||
|
extension WebSocket {
|
||||||
|
var isConnected: Bool {
|
||||||
|
return state == .open
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnect() {
|
||||||
|
do {
|
||||||
|
try close()
|
||||||
|
} catch {
|
||||||
|
DefaultSocketLogger.Logger.error("Error closing ws", type: "SocketEngineWebsocket")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func write(string: String) {
|
||||||
|
do {
|
||||||
|
try send(string)
|
||||||
|
} catch {
|
||||||
|
DefaultSocketLogger.Logger.error("Error sending string", type: "SocketEngineWebsocket", args: string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func write(data: Data) {
|
||||||
|
do {
|
||||||
|
try data.withUnsafeBytes {(bytes: UnsafePointer<UInt8>) in
|
||||||
|
try self.send(Array(UnsafeBufferPointer(start: bytes, count: data.count)))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
DefaultSocketLogger.Logger.error("Error sending data", type: "SocketEngineWebsocket", args: data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@ -22,8 +22,11 @@
|
|||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
|
import Dispatch
|
||||||
import Foundation
|
import Foundation
|
||||||
|
#if !os(Linux)
|
||||||
import StarscreamSocketIO
|
import StarscreamSocketIO
|
||||||
|
#endif
|
||||||
|
|
||||||
enum JSONError : Error {
|
enum JSONError : Error {
|
||||||
case notArray
|
case notArray
|
||||||
@ -111,9 +114,9 @@ extension String {
|
|||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
func toNSDictionary() throws -> NSDictionary {
|
func toDictionary() throws -> [String: Any] {
|
||||||
guard let binData = data(using: .utf16, allowLossyConversion: false) else { return [:] }
|
guard let binData = data(using: .utf16, allowLossyConversion: false) else { return [:] }
|
||||||
guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? NSDictionary else {
|
guard let json = try JSONSerialization.jsonObject(with: binData, options: .allowFragments) as? [String: Any] else {
|
||||||
throw JSONError.notNSDictionary
|
throw JSONError.notNSDictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,10 +62,12 @@ public extension SocketLogger {
|
|||||||
private func abstractLog(_ logType: String, message: String, type: String, args: [Any]) {
|
private func abstractLog(_ logType: String, message: String, type: String, args: [Any]) {
|
||||||
guard log else { return }
|
guard log else { return }
|
||||||
|
|
||||||
|
#if !os(Linux)
|
||||||
let newArgs = args.map({arg -> CVarArg in String(describing: arg)})
|
let newArgs = args.map({arg -> CVarArg in String(describing: arg)})
|
||||||
let messageFormat = String(format: message, arguments: newArgs)
|
let messageFormat = String(format: message, arguments: newArgs)
|
||||||
|
|
||||||
NSLog("\(logType) \(type): %@", messageFormat)
|
NSLog("\(logType) \(type): %@", messageFormat)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user