@@ -111,13 +111,14 @@ public class WebSocket : NSObject, NSStreamDelegate {
111
111
public var selfSignedSSL = false
112
112
private var security : SSLSecurity ?
113
113
public var enabledSSLCipherSuites : [ SSLCipherSuite ] ?
114
+ public var origin : String ?
114
115
public var isConnected : Bool {
115
116
return connected
116
117
}
118
+ public var currentURL : NSURL { return url}
117
119
private var url : NSURL
118
120
private var inputStream : NSInputStream ?
119
121
private var outputStream : NSOutputStream ?
120
- private var isRunLoop = false
121
122
private var connected = false
122
123
private var isCreated = false
123
124
private var writeQueue = NSOperationQueue ( )
@@ -126,26 +127,24 @@ public class WebSocket : NSObject, NSStreamDelegate {
126
127
private var fragBuffer : NSData ?
127
128
private var certValidated = false
128
129
private var didDisconnect = false
130
+ //the shared processing queue used for all websocket
131
+ private static let sharedWorkQueue = dispatch_queue_create ( " com.vluxe.starscream.websocket " , DISPATCH_QUEUE_SERIAL)
129
132
130
133
//used for setting protocols.
131
134
public init ( url: NSURL , protocols: [ String ] ? = nil ) {
132
135
self . url = url
136
+ self . origin = url. absoluteString
133
137
writeQueue. maxConcurrentOperationCount = 1
134
138
optionalProtocols = protocols
135
139
}
136
140
137
141
///Connect to the websocket server on a background thread
138
142
public func connect( ) {
139
143
guard !isCreated else { return }
140
-
141
- dispatch_async ( queue) { [ weak self] in
142
- self ? . didDisconnect = false
143
- }
144
- dispatch_async ( dispatch_get_global_queue ( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ) ) { [ weak self] in
145
- self ? . isCreated = true
146
- self ? . createHTTPRequest ( )
147
- self ? . isCreated = false
148
- }
144
+ didDisconnect = false
145
+ isCreated = true
146
+ createHTTPRequest ( )
147
+ isCreated = false
149
148
}
150
149
151
150
/**
@@ -175,20 +174,22 @@ public class WebSocket : NSObject, NSStreamDelegate {
175
174
176
175
///write a string to the websocket. This sends it as a text frame.
177
176
public func writeString( str: String ) {
177
+ guard isConnected else { return }
178
178
dequeueWrite ( str. dataUsingEncoding ( NSUTF8StringEncoding) !, code: . TextFrame)
179
179
}
180
180
181
181
///write binary data to the websocket. This sends it as a binary frame.
182
182
public func writeData( data: NSData ) {
183
+ guard isConnected else { return }
183
184
dequeueWrite ( data, code: . BinaryFrame)
184
185
}
185
186
186
187
//write a ping to the websocket. This sends it as a control frame.
187
188
//yodel a sound to the planet. This sends it as an astroid. http://youtu.be/Eu5ZJELRiJ8?t=42s
188
189
public func writePing( data: NSData ) {
190
+ guard isConnected else { return }
189
191
dequeueWrite ( data, code: . Ping)
190
192
}
191
- //private methods below!
192
193
193
194
//private method that starts the connection
194
195
private func createHTTPRequest( ) {
@@ -211,7 +212,9 @@ public class WebSocket : NSObject, NSStreamDelegate {
211
212
}
212
213
addHeader ( urlRequest, key: headerWSVersionName, val: headerWSVersionValue)
213
214
addHeader ( urlRequest, key: headerWSKeyName, val: generateWebSocketKey ( ) )
214
- addHeader ( urlRequest, key: headerOriginName, val: url. absoluteString)
215
+ if let origin = origin {
216
+ addHeader ( urlRequest, key: headerOriginName, val: origin)
217
+ }
215
218
addHeader ( urlRequest, key: headerWSHostName, val: " \( url. host!) : \( port!) " )
216
219
for (key, value) in headers {
217
220
addHeader ( urlRequest, key: key, val: value)
@@ -221,10 +224,12 @@ public class WebSocket : NSObject, NSStreamDelegate {
221
224
initStreamsWithData ( serializedRequest, Int ( port!) )
222
225
}
223
226
}
227
+
224
228
//Add a header to the CFHTTPMessage by using the NSString bridges to CFString
225
229
private func addHeader( urlRequest: CFHTTPMessage , key: NSString , val: NSString ) {
226
230
CFHTTPMessageSetHeaderFieldValue ( urlRequest, key, val)
227
231
}
232
+
228
233
//generate a websocket key as needed in rfc
229
234
private func generateWebSocketKey( ) -> String {
230
235
var key = " "
@@ -237,6 +242,7 @@ public class WebSocket : NSObject, NSStreamDelegate {
237
242
let baseKey = data? . base64EncodedStringWithOptions ( NSDataBase64EncodingOptions ( rawValue: 0 ) )
238
243
return baseKey!
239
244
}
245
+
240
246
//Start the stream connection and write the data to the output stream
241
247
private func initStreamsWithData( data: NSData , _ port: Int ) {
242
248
//higher level API we will cut over to at some point
@@ -283,15 +289,16 @@ public class WebSocket : NSObject, NSStreamDelegate {
283
289
}
284
290
}
285
291
}
286
- isRunLoop = true
287
- inStream. scheduleInRunLoop ( NSRunLoop . currentRunLoop ( ) , forMode: NSDefaultRunLoopMode)
288
- outStream. scheduleInRunLoop ( NSRunLoop . currentRunLoop ( ) , forMode: NSDefaultRunLoopMode)
292
+ CFReadStreamSetDispatchQueue( inStream, WebSocket . sharedWorkQueue)
293
+ CFWriteStreamSetDispatchQueue ( outStream, WebSocket . sharedWorkQueue)
289
294
inStream. open ( )
290
295
outStream. open ( )
291
296
let bytes = UnsafePointer < UInt8 > ( data. bytes)
292
- outStream. write ( bytes, maxLength: data. length)
293
- while ( isRunLoop) {
294
- NSRunLoop . currentRunLoop ( ) . runMode ( NSDefaultRunLoopMode, beforeDate: NSDate . distantFuture ( ) as NSDate )
297
+ writeQueue. addOperationWithBlock {
298
+ while !outStream. hasSpaceAvailable {
299
+ usleep ( 100 ) //wait until the socket is ready
300
+ }
301
+ outStream. write ( bytes, maxLength: data. length)
295
302
}
296
303
}
297
304
//delegate for the stream methods. Processes incoming bytes
@@ -324,18 +331,16 @@ public class WebSocket : NSObject, NSStreamDelegate {
324
331
private func disconnectStream( error: NSError? ) {
325
332
writeQueue. waitUntilAllOperationsAreFinished ( )
326
333
if let stream = inputStream {
327
- stream . removeFromRunLoop ( NSRunLoop . currentRunLoop ( ) , forMode : NSDefaultRunLoopMode )
334
+ CFReadStreamSetDispatchQueue ( stream , nil )
328
335
stream. close ( )
329
336
}
330
337
if let stream = outputStream {
331
- stream . removeFromRunLoop ( NSRunLoop . currentRunLoop ( ) , forMode : NSDefaultRunLoopMode )
338
+ CFWriteStreamSetDispatchQueue ( stream , nil )
332
339
stream. close ( )
333
340
}
334
341
outputStream = nil
335
- isRunLoop = false
336
342
certValidated = false
337
343
doDisconnect ( error)
338
- connected = false
339
344
}
340
345
341
346
///handles the incoming bytes and sending them to the proper processing method
@@ -345,24 +350,13 @@ public class WebSocket : NSObject, NSStreamDelegate {
345
350
let length = inputStream!. read ( buffer, maxLength: BUFFER_MAX)
346
351
347
352
guard length > 0 else { return }
348
-
349
- if !connected {
350
- connected = processHTTP ( buffer, bufferLen: length)
351
- if !connected {
352
- let response = CFHTTPMessageCreateEmpty ( kCFAllocatorDefault, false ) . takeRetainedValue ( )
353
- CFHTTPMessageAppendBytes ( response, buffer, length)
354
- let code = CFHTTPMessageGetResponseStatusCode ( response)
355
- doDisconnect ( errorWithDetail ( " Invalid HTTP upgrade " , code: UInt16 ( code) ) )
356
- }
357
- } else {
358
- var process = false
359
- if inputQueue. count == 0 {
360
- process = true
361
- }
362
- inputQueue. append ( NSData ( bytes: buffer, length: length) )
363
- if process {
364
- dequeueInput ( )
365
- }
353
+ var process = false
354
+ if inputQueue. count == 0 {
355
+ process = true
356
+ }
357
+ inputQueue. append ( NSData ( bytes: buffer, length: length) )
358
+ if process {
359
+ dequeueInput ( )
366
360
}
367
361
}
368
362
///dequeue the incoming input so it is processed in order
@@ -378,12 +372,34 @@ public class WebSocket : NSObject, NSStreamDelegate {
378
372
self . fragBuffer = nil
379
373
}
380
374
let buffer = UnsafePointer < UInt8 > ( work. bytes)
381
- processRawMessage ( buffer, bufferLen: work. length)
375
+ let length = work. length
376
+ if !connected {
377
+ processTCPHandshake ( buffer, bufferLen: length)
378
+ } else {
379
+ processRawMessage ( buffer, bufferLen: length)
380
+ }
382
381
inputQueue = inputQueue. filter { $0 != data}
383
382
dequeueInput ( )
384
383
}
384
+
385
+ //handle checking the inital connection status
386
+ private func processTCPHandshake( buffer: UnsafePointer < UInt8 > , bufferLen: Int) {
387
+ let code = processHTTP ( buffer, bufferLen: bufferLen)
388
+ switch code {
389
+ case 0 :
390
+ connected = true
391
+ dispatch_async ( queue) { [ weak self] in
392
+ guard let s = self else { return }
393
+ s. onConnect ? ( )
394
+ s. delegate? . websocketDidConnect ( s)
395
+ }
396
+ case - 1 : break //do nothing, we are going to collect more data
397
+ default :
398
+ doDisconnect ( errorWithDetail ( " Invalid HTTP upgrade " , code: UInt16 ( code) ) )
399
+ }
400
+ }
385
401
///Finds the HTTP Packet in the TCP stream, by looking for the CRLF.
386
- private func processHTTP( buffer: UnsafePointer < UInt8 > , bufferLen: Int) - > Bool {
402
+ private func processHTTP( buffer: UnsafePointer < UInt8 > , bufferLen: Int) - > Int {
387
403
let CRLFBytes = [ UInt8 ( ascii: " \r " ) , UInt8 ( ascii: " \n " ) , UInt8 ( ascii: " \r " ) , UInt8 ( ascii: " \n " ) ]
388
404
var k = 0
389
405
var totalSize = 0
@@ -399,39 +415,37 @@ public class WebSocket : NSObject, NSStreamDelegate {
399
415
}
400
416
}
401
417
if totalSize > 0 {
402
- if validateResponse ( buffer, bufferLen: totalSize) {
403
- dispatch_async ( queue) { [ weak self] in
404
- guard let s = self else { return }
405
- s. onConnect ? ( )
406
- s. delegate? . websocketDidConnect ( s)
407
- }
408
- totalSize += 1 //skip the last \n
409
- let restSize = bufferLen - totalSize
410
- if restSize > 0 {
411
- processRawMessage ( ( buffer+ totalSize) , bufferLen: restSize)
412
- }
413
- return true
418
+ let code = validateResponse ( buffer, bufferLen: totalSize)
419
+ if code != 0 {
420
+ return code
421
+ }
422
+ totalSize += 1 //skip the last \n
423
+ let restSize = bufferLen - totalSize
424
+ if restSize > 0 {
425
+ processRawMessage ( ( buffer+ totalSize) , bufferLen: restSize)
414
426
}
427
+ return 0 //success
415
428
}
416
- return false
429
+ return - 1 //was unable to find the full TCP header
417
430
}
418
431
419
432
///validates the HTTP is a 101 as per the RFC spec
420
- private func validateResponse( buffer: UnsafePointer < UInt8 > , bufferLen: Int) - > Bool {
433
+ private func validateResponse( buffer: UnsafePointer < UInt8 > , bufferLen: Int) - > Int {
421
434
let response = CFHTTPMessageCreateEmpty ( kCFAllocatorDefault, false ) . takeRetainedValue ( )
422
435
CFHTTPMessageAppendBytes ( response, buffer, bufferLen)
423
- if CFHTTPMessageGetResponseStatusCode ( response) != 101 {
424
- return false
436
+ let code = CFHTTPMessageGetResponseStatusCode ( response)
437
+ if code != 101 {
438
+ return code
425
439
}
426
440
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields ( response) {
427
441
let headers = cfHeaders. takeRetainedValue ( ) as NSDictionary
428
442
if let acceptKey = headers [ headerWSAcceptName] as? NSString {
429
443
if acceptKey. length > 0 {
430
- return true
444
+ return 0
431
445
}
432
446
}
433
447
}
434
- return false
448
+ return - 1
435
449
}
436
450
437
451
///read a 16 bit big endian value from a buffer
@@ -687,8 +701,6 @@ public class WebSocket : NSObject, NSStreamDelegate {
687
701
}
688
702
///used to write things to the stream
689
703
private func dequeueWrite( data: NSData, code: OpCode) {
690
- guard isConnected else { return }
691
-
692
704
writeQueue. addOperationWithBlock { [ weak self] in
693
705
//stream isn't ready, let's wait
694
706
guard let s = self else { return }
@@ -720,9 +732,6 @@ public class WebSocket : NSObject, NSStreamDelegate {
720
732
}
721
733
var total = 0
722
734
while true {
723
- if !s. isConnected {
724
- break
725
- }
726
735
guard let outStream = s. outputStream else { break }
727
736
let writeBuffer = UnsafePointer < UInt8 > ( frame!. bytes+ total)
728
737
let len = outStream. write ( writeBuffer, maxLength: offset- total)
@@ -750,17 +759,16 @@ public class WebSocket : NSObject, NSStreamDelegate {
750
759
///used to preform the disconnect delegate
751
760
private func do Disconnect( error: NSError? ) {
752
761
guard !didDisconnect else { return }
753
-
762
+ didDisconnect = true
763
+ connected = false
754
764
dispatch_async ( queue) { [ weak self] in
755
765
guard let s = self else { return }
756
- s. didDisconnect = true
757
766
s. onDisconnect ? ( error)
758
767
s. delegate? . websocketDidDisconnect ( s, error: error)
759
768
}
760
769
}
761
770
762
771
}
763
-
764
772
private class SSLCert {
765
773
var certData : NSData ?
766
774
var key : SecKeyRef ?
0 commit comments