Network.framework (websocket): read response headers set by the server

I’m trying to figure it out how a ws client can read additional headers set by the WebSocket server during the handshake.

Websocket Server (built using the NWProtocolWebSocket)

Code Block
let wsOptions = NWProtocolWebSocket.Options()
wsOptions.setClientRequestHandler(serverQueue) { (_, headers) -> NWProtocolWebSocket.Response in
let additionalHeaders = [("custom-header", "hi there")]
return .init(status: .accept, subprotocol: nil, additionalHeaders: additionalHeaders)
}


Websocket Client (built using the NWProtocolWebSocket)

I'm aware that NWProtocolWebSocket.Metadata has an additionalServerHeaders but I don't know how to access it.

I’m trying to figure it out how a ws client can read additional headers set by the WebSocket server during the handshake.

Are you wanting to know this to look for the connection upgrade header during the WebSocket handshake?


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
I would like to look for additional custom HTTP headers set by the server in the handshake response. (not the Connection: Upgrade header, if you're referring to that).

Thanks

I would like to look for additional custom HTTP headers set by the server in the handshake response

I am not aware of any way to do this during the handshake. After the connection is setup and the handshake is complete there could be low level opportunities to read the actual frame data and then convert that into header information with NWProtocolFramerImplementation, but for the handshake this is an enhancement request.

Please follow up with your Feedback ID.

One last thing, I'm not sure what the context of this is, but you may want to also take a look at SwiftNIO with Network Framework as they have some custom websocket routines built into their stack that may support this.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com
Thanks for the suggestions; I will certanly look into them.

I am not aware of any way to do this during the handshake. 

Ok, I thought it could be possible using the additionalServerHeaders defined in the NWProtocolWebSocket.Metadata.

Please follow up with your Feedback ID.

FB8782882
No problem. I looked into additionalServerHeaders a bit and this does certainly look like something you can add on the client side of the connection with:

Code Block
let webSocketOptions = NWProtocolWebSocket.Options()
webSocketOptions.setAdditionalHeaders([(name: "testing", value: "mytestvalue")])


However, this is to set addition headers during the initial handshake.

I do see your bug internally and have copied myself on it for more info. Thanks.


Matt Eaton
DTS Engineering, CoreOS
meaton3@apple.com

 I looked into additionalServerHeaders a bit and this does certainly look like something you can add on the client side of the connection

Reading the documentation, additionalServerHeaders is supposed to contain “additional HTTP headers sent by the server during the WebSocket handshake.”; I don't think they can be set client side. (But I'm totally new to the framework so I trust your words much more than my assumptions).

To give you more context:

If, client side, I set some headers:

Code Block
let wsOptions = NWProtocolWebSocket.Options()
wsOptions.setAdditionalHeaders([("custom-client-header", "test-client”)])


I’m able to retrieve them server side (the test server is implemented with Network.framework locally), like so:

Code Block
let wsOptions = NWProtocolWebSocket.Options()
wsOptions.setClientRequestHandler(serverQueue) { (_, headers) -> NWProtocolWebSocket.Response in
print(headers) /* [(name: "custom-client-header", value: "test-client"), (name: "Host", value: "localhost")] ✅ */
let additionalServerHeaders = [("custom-server-header", “test-server”)] /* set some custom headers */
return.init(status: .accept, subprotocol: nil, additionalHeaders: additionalServerHeaders)
}


At this point, since I’ve set new headers server side during the handshake, I thought I could access them client side somehow:

My first try (client side) was:

Code Block
connection = NWConnection(…)
connection.stateUpdateHandler = { [weak self] state in
guard let self = self else { return }
/* the cast to NWProtocolWebSocket.Metadata always fails */
if let metadata = self.connection.metadata(definition: NWProtocolWebSocket.definition) as? NWProtocolWebSocket.Metadata {
print(metadata.additionalServerHeaders)
}
}


My second one was:

Code Block
connection.receiveMessage { [weak self] (data, context, isComplete, error) in
if let context = context, let metadata = context.protocolMetadata.first as? NWProtocolWebSocket.Metadata {
print(metadata.additionalServerHeaders) /* nil */
}
}


In short: I didn't find a way to proper access the NWProtocolWebSocket.Metadata additionalServerHeaders.

Again, thank you for spending your time looking into this.
Network.framework (websocket): read response headers set by the server
 
 
Q