Skip to content

Commit

Permalink
Merge pull request #337 from hishnash/AdvancedDelegate
Browse files Browse the repository at this point in the history
Add Advanced Delegate and Custom HTTP methods
  • Loading branch information
daltoniam authored Jun 24, 2017
2 parents bb11123 + bce64da commit 7e517c2
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 6 deletions.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,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:
Expand Down Expand Up @@ -298,6 +313,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?
Expand Down
57 changes: 51 additions & 6 deletions Source/WebSocket.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -102,12 +112,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
Expand All @@ -116,18 +129,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
Expand Down Expand Up @@ -274,7 +313,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
Expand Down Expand Up @@ -306,6 +345,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)
}
}

Expand Down Expand Up @@ -572,6 +612,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)
}
}
Expand All @@ -592,6 +633,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
}
Expand Down Expand Up @@ -891,6 +933,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 {
Expand All @@ -900,6 +943,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)
}
}
}
Expand Down Expand Up @@ -1020,6 +1064,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)
}
Expand Down

0 comments on commit 7e517c2

Please sign in to comment.