I am able to build prototype of WebSocket server in iOS device using below sample example. it worked
https://github.com/apple/swift-nio/blob/main/Sources/NIOWebSocketServer/main.swift
But I tried enhancing the logic to support SSL connection using approach given in following link: https://cocoapods.org/pods/SwiftNIOSSL. it didn't work when tried connection through client (postman) to url: wss://127.0.01:60259, it gives below error
Error: write EPROTO 5812094680:error:100000f7:SSL routines:OPENSSL_internal:WRONG_VERSION_NUMBER:../../../../src/third_party/boringssl/src/ssl/tls_record.cc:242:
When I use url ws://127.0.01:60259 (without SSL), it connects. Not sure what is missing in code for SSL connection
Note: generated certificate and private key is valid as I have tested same with WebSocket server created using ws library part of node based application
Please help here. below is the code (Removed irrelevant/personal code)
import Foundation
import NIOCore
import NIOPosix
import NIOHTTP1
import NIOWebSocket
import NIOSSL
private final class HTTPHandler: ChannelInboundHandler, RemovableChannelHandler {
private var responseBody: ByteBuffer!
func handlerAdded(context: ChannelHandlerContext) {
//some code
}
func handlerRemoved(context: ChannelHandlerContext) {
self.responseBody = nil
}
func channelActive(context: ChannelHandlerContext) {
//save of client channel
}
func channelInactive(context: ChannelHandlerContext) {
//removing of client channel
}
func channelRead(context: ChannelHandlerContext, data: NIOAny) {
Handle read
}
private func respond405(context: ChannelHandlerContext) {
//handle respond
}
}
private final class WebSocketServerHandler: ChannelInboundHandler {
func channelInactive(context: ChannelHandlerContext) {
// Remove the disconnected client from the list of connected clients
}
public func handlerAdded(context: ChannelHandlerContext) {
self.sendMessage(context: context)
}
public func channelRead(context: ChannelHandlerContext, data: NIOAny) {
//read messages
}
public func channelReadComplete(context: ChannelHandlerContext) {
context.flush()
}
private func receivedClose(context: ChannelHandlerContext, frame: WebSocketFrame) {
// Handle a received close frame. In websockets, we're just going to send the close
}
}
//This start function eventually called when a button is clicked and its part of a class
func start() {
do{
let wshandler = WebSocketServerHandler()
NioWSServerSSL.wshandler = wshandler
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
let upgrader = NIOWebSocketServerUpgrader(shouldUpgrade: { (channel: Channel, head: HTTPRequestHead) in channel.eventLoop.makeSucceededFuture(HTTPHeaders()) },
upgradePipelineHandler: { (channel: Channel, _: HTTPRequestHead) in
channel.pipeline.addHandler(wshandler)
})
//Note: generated key.pem and cert.pem using openssl and this is just for development purpose
let path = ">>>path where the key.pem and cert.pem is stores"
let configuration = TLSConfiguration.makeServerConfiguration(
certificateChain: try NIOSSLCertificate.fromPEMFile("\(path)/cert.pem").map { .certificate($0) },
privateKey: .file("\(path)/key.pem")
)
let sslContext = try NIOSSLContext(configuration: configuration)
let bootstrap = ServerBootstrap(group: group)
// Specify backlog and enable SO_REUSEADDR for the server itself
.serverChannelOption(ChannelOptions.backlog, value: 256)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
// Set the handlers that are applied to the accepted Channels
.childChannelInitializer { channel in
let httpHandler = HTTPHandler()
let sslHandler = NIOSSLServerHandler(context: sslContext)
let config: NIOHTTPServerUpgradeConfiguration = (
upgraders: [ upgrader ],
completionHandler: { _ in
channel.pipeline.removeHandler(httpHandler, promise: nil)
}
)
return channel.pipeline.configureHTTPServerPipeline(withServerUpgrade: config).flatMap {
channel.pipeline.addHandler(httpHandler).flatMap {
channel.pipeline.addHandler(sslHandler)
}
}
}
// Enable SO_REUSEADDR for the accepted Channels
.childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
let channel = try bootstrap.bind(host: "127.0.0.1", port: 60259).wait()
//let channel = try bootstrap.bind(host: "127.0.0.1", port: 12346).wait()
guard let localAddress = channel.localAddress else {
fatalError("Address was unable to bind. Please check that the socket was not closed or that the address family was understood.")
}
print("Server started and listening on \(localAddress)")
}
catch {
print("Error with Web socket connection \(error)")
}
}