Search code examples
iossocketswebsocketnsstreamnsinputstream

How to connect to wss socket where host name has additional path using NSStream?


I'm trying to connect to wss socket, and the host name looks like this: "myhostname.com/ws/v2". Here is how I start the connection:

let host = "myhostname.com/ws/v2"

CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host as CFString, 443, &readStream, &writeStream)

inputStream = readStream!.takeRetainedValue()
outputStream = writeStream!.takeRetainedValue()

outputStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL, forKey: Stream.PropertyKey.socketSecurityLevelKey)
inputStream.setProperty(StreamSocketSecurityLevel.negotiatedSSL, forKey: Stream.PropertyKey.socketSecurityLevelKey)

inputStream.schedule(in: .current, forMode: .commonModes)
outputStream.schedule(in: .current, forMode: .commonModes)

inputStream.delegate = self
outputStream.delegate = self

inputStream.open()
outputStream.open()

This fails with an error: The operation couldn’t be completed. (kCFErrorDomainCFNetwork error 1.)

However, if I remove the path from the host name, so it looks like this: myhostname.com then in my delegate I get an event openCompleted. However, it doesn't respond to my messages after that, I assume it's because I'm connected to a wrong socket, since I removed the path.

What is the proper way of connecting to a socket when the host name has an additional path?


Solution

  • myhostname.com/ws/v2 is not a hostname. It is an (incomplete) URL (the complete URL is wss://myhostname.com/ws/v2). The hostname is just myhostname.com, and the Websocket path on that host is just /ws/v2.

    The WebSockets handshake uses HTTP/S, so it is not enough to just connect to the host with an NSStream. You have to connect a TCP socket to the host and port, then negotiate an SSL/TLS handshake if using WSS, then use HTTP to request the path asking for an Upgrade to WebSocket, and only if a successful HTTP 101 reply is returned then perform the WebSocket handshake.

    That is a lot of work to do manually. You really should be using an actual WebSocket client library instead. There are plenty available.