more linux work

This commit is contained in:
Erik Little 2017-09-16 11:55:06 -04:00
parent afc5a561d0
commit 95b3bfc802
No known key found for this signature in database
GPG Key ID: 4930B7C5FBC1A69D
3 changed files with 169 additions and 63 deletions

View File

@ -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,17 @@ 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
#if !os(Linux)
/// A custom security validator for Starscream. Useful for SSL pinning.
public private(set) var security: SSLSecurity?
#endif
/// 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 +131,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 +148,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
@ -308,32 +320,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)
@ -486,6 +472,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 +625,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 {

View File

@ -24,7 +24,11 @@
// //
import Foundation import Foundation
#if !os(Linux)
import StarscreamSocketIO import StarscreamSocketIO
#else
import WebSockets
#endif
/// Specifies a SocketEngine. /// Specifies a SocketEngine.
@objc public protocol SocketEngineSpec { @objc public protocol SocketEngineSpec {
@ -40,6 +44,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 +71,14 @@ 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 }
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 +95,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.
/// ///

View File

@ -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,74 @@ 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))
let stream = secure ? try TLS.InternetSocket(socket, TLS.Context(.client)) : socket
try WebSocket.background(to: connectURL, using: stream) {[weak self] ws in
guard let this = self else { return }
this.ws = ws
this.attachWebSocketHandlers()
this.handleWSConnect()
}
} catch {
DefaultSocketLogger.Logger.error("Error connecting socket", type: "SocketEngineWebsocket")
}
#endif
}
func probeWebSocket() { func probeWebSocket() {
if ws?.isConnected ?? false { if ws?.isConnected ?? false {
sendWebSocketMessage("probe", withType: .ping, withData: []) sendWebSocketMessage("probe", withType: .ping, withData: [])
@ -67,6 +147,7 @@ extension SocketEngineWebsocket {
// MARK: Starscream delegate methods // MARK: Starscream delegate methods
#if !os(Linux)
/// Delegate method for when a message is received. /// Delegate method for when a message is received.
public func websocketDidReceiveMessage(socket: WebSocket, text: String) { public func websocketDidReceiveMessage(socket: WebSocket, text: String) {
parseEngineMessage(text) parseEngineMessage(text)
@ -76,4 +157,16 @@ extension SocketEngineWebsocket {
public func websocketDidReceiveData(socket: WebSocket, data: Data) { public func websocketDidReceiveData(socket: WebSocket, data: Data) {
parseEngineData(data) parseEngineData(data)
} }
#endif
} }
#if os(Linux)
/// SSLSecurity does nothing on Linux.
public final class SSLSecurity { }
extension WebSocket {
var isConnected: Bool {
return state == .open
}
}
#endif