diff --git a/README.md b/README.md index 7120f775..65f965ab 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,21 @@ socket.headers["Sec-WebSocket-Version"] = "14" socket.headers["My-Awesome-Header"] = "Everything is Awesome!" ``` +### Custom HTTP Method + +Your server may use a different HTTP method when connecting to the websocket: + +```swift +socket.httpMethod = .post +``` +you can use a custom string: + +```swift +socket.httpMethod = .custom(value: "mycustomhttpmethod") +``` + + + ### Protocols If you need to specify a protocol, simple add it to the init: @@ -286,6 +301,46 @@ Add the `Starscream.xcodeproj` to your Xcode project. Once that is complete, in If you are running this in an OSX app or on a physical iOS device you will need to make sure you add the `Starscream.framework` to be included in your app bundle. To do this, in Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar. In the tab bar at the top of that window, open the "Build Phases" panel. Expand the "Link Binary with Libraries" group, and add `Starscream.framework`. Click on the + button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add `Starscream.framework` respectively. + +## WebSocketAdvancedDelegate +The advanced delegate acts just like the simpler delegate but provides some additional information on the connection and incoming frames. + +```swift +socket.advancedDelegate = self +``` + +In most cases you do not need the extra info and should use the normal delegate. + +#### websocketDidReceiveMessage +```swift +func websocketDidReceiveMessage(socket: WebSocket, text: String, response: WebSocket.WSResponse { + print("got some text: \(text)") + print("First frame for this message arrived on \(response.firstFrame)") +} +``` + +#### websocketDidReceiveData +```swift +func websocketDidReceiveData(socket: WebSocket, data: Date, response: WebSocket.WSResponse) { + print("got some data it long: \(data.count)") + print("A total of \(response.frameCount) frames were used to send this data") +} +``` + +#### websocketHttpUpgrade +These methods are called when the HTTP upgrade request is sent and when the response returns. +```swift +func websocketHttpUpgrade(socket: WebSocket, request: CFHTTPMessage) { + print("the http request was sent we can check the raw http if we need to") +} +``` + +```swift +func websocketHttpUpgrade(socket: WebSocket, response: CFHTTPMessage) { + print("the http response has returned.") +} +``` + ## TODOs - [ ] WatchOS? diff --git a/Source/WebSocket.swift b/Source/WebSocket.swift index 469da6be..798d661b 100644 --- a/Source/WebSocket.swift +++ b/Source/WebSocket.swift @@ -38,9 +38,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 @@ -99,12 +109,15 @@ open class WebSocket : NSObject, StreamDelegate { 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 @@ -113,18 +126,44 @@ 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 @@ -258,7 +297,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 @@ -286,6 +325,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) } } @@ -552,6 +592,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) } } @@ -572,6 +613,7 @@ 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 } @@ -817,6 +859,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 { @@ -826,6 +869,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) } } } @@ -933,6 +977,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) }