简体   繁体   中英

Swift-NIO secured websocket server

I am trying to create websocket server and client in my iOS app, which i successfully managed to do with the help of sample implementation here. ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer ) - so current working situation is, i run the websocket server when app launches and then I load the client in a webview which can connect to it.

Now my problem is I want my server to secured websocket server (Basically connect to the websocket server from a HTTPS html page)

I am new to network programming and Swift-nio documentation is lacking to say the least. As far as I understand I could use ( https://github.com/apple/swift-nio-transport-services )

I found this thread which is exactly what I need -https://github.com/apple/swift-nio-transport-services/issues/39 - I could disable the TLS authentication as I dont care in my usecase as long as I could get the websocket connected.

So my question is how to I extend my client ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketClient ) and server ( https://github.com/apple/swift-nio/tree/master/Sources/NIOWebSocketServer ) to use swift-nio-transport-service.

I could add the NIOSSLContext and stuff but I think I need to add the EventLoopGroup and new bootstrap methods. I know the answers is right there.... but I just cannot seem to pinpoint it.

Any pointer would be appreciated.

Thanks.

To translate a simple NIO Server to a NIOTransportServices one, you need to make the following changes:

  1. Add a dependency on NIOTransportServices to your server.
  2. Change MultiThreadedEventLoopGroup to NIOTSEventLoopGroup .
  3. Change ClientBootstrap to NIOTSConnectionBootstrap .
  4. Change ServerBootstrap to NIOTSListenerBootstrap .
  5. Build and run your code.

Some ChannelOption s don't work in NIOTransportServices , but most do: the easiest way to confirm that things are behaving properly is to quickly test the common flow.

This doesn't add any extra functionality to your application, but it does give you the same functionality using the iOS APIs.

To add TLS to either NIOTSConnectionBootstrap or NIOTSListenerBootstrap , you use the .tlsOptions function. For example:

NIOTSListenerBootstrap(group: group)
    .tlsOptions(myTLSOptions())

Configuring a NWProtocolTLS.Options is a somewhat tricky thing to do. You need to obtain a SecIdentity , which requires interacting with the keychain. Quinn has discussed this somewhat here .

Once you have a SecIdentity , you can use it like so:

func myTLSOptions() -> NWProtocolTLS.Options {
    let options = NWProtocolTLS.Options()
    let yourSecIdentity = // you have to implement something here
    sec_protocol_options_set_local_identity(options.securityProtocolOptions, sec_identity_create(yourSecIdentity)
    return options
}

Once you have that code written, everything should go smoothly!


As an extension, if you wanted to secure a NIO server on Linux, you can do so using swift-nio-ssl . This has separate configuration as the keychain APIs are not available, and so you do a lot more loading of keys and certificates from files.

I needed a secure websocket without using SecIdentity or NIOTransportServices , so based on @Lukasa's hint about swift-nio-ssl I cobbled together an example that appears to work correctly.

I dunno if it's correct, but I'm putting it here in case someone else can benefit. Error-handling and aborting when the try 's fail is left out for brevity.

let configuration = TLSConfiguration.forServer(certificateChain: try! NIOSSLCertificate.fromPEMFile("/path/to/your/tlsCert.pem").map { .certificate($0) }, privateKey: .file("/path/to/your/tlsKey.pem"))
let sslContext = try! NIOSSLContext(configuration: configuration)
            
let upgradePipelineHandler: (Channel, HTTPRequestHead) -> EventLoopFuture<Void> = { channel, req in
                
    WebSocket.server(on: channel) { ws in

       ws.send("You have connected to WebSocket")

       ws.onText { ws, string in
           print("Received text: \(string)")
       }
                
       ws.onBinary { ws, buffer in
           // We don't accept any Binary data
       }
                
       ws.onClose.whenSuccess { value in
           print("onClose")
       }
   }
}

self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2)
let port: Int = 5759
let promise = self.eventLoopGroup!.next().makePromise(of: String.self)
            
_ = try? ServerBootstrap(group: self.eventLoopGroup!)
                
    // Specify backlog and enable SO_REUSEADDR for the server itself
    .serverChannelOption(ChannelOptions.backlog, value: 256)
    .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
                
    .childChannelInitializer { channel in
                    
        let handler = NIOSSLServerHandler(context: sslContext)
        _ = channel.pipeline.addHandler(handler)
              
        let webSocket = NIOWebSocketServerUpgrader(
            shouldUpgrade: { channel, req in
                return channel.eventLoop.makeSucceededFuture([:])
            },
            upgradePipelineHandler: upgradePipelineHandler
        )
              
        return channel.pipeline.configureHTTPServerPipeline(
            withServerUpgrade: (
                upgraders: [webSocket],
                completionHandler: { ctx in
                     // complete
                })
        )
    }.bind(host: "0.0.0.0", port: port).wait()

_ = try! promise.futureResult.wait()
try! server.close(mode: .all).wait()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM