Universal Bootstrap NIOTS with WebSockets

I am developing a WebSocket swift package that is written on top of NIOTS. I am running into a problem where the client connects to the server, but then immediately disconnects in the defer block. I am not sure what is going on, but basically the application is as follows.

```
public func connect() throws {
    var group: EventLoopGroup? = nil
    #if canImport(Network)
    if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) {
        group = NIOTSEventLoopGroup()
    } else {
        print("Sorry, your OS is too old for Network.framework.")
        exit(0)
    }
    #else
    group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
    print("Sorry, no Network.framework on your OS.")
    #endif
    
    defer {
        try? group?.syncShutdownGracefully()
    }
    
    //Map the Event Loop Group to our Provider
    let provider: EventLoopGroupManager.Provider = group.map { .shared($0)} ?? .createNew
    guard let upgradePromise = group?.next().makePromise(of: Void.self) else {
        throw MyErrors.optionalELGError("Our Event Loop Group was nil")
    }
    guard let url = URL(string: self.url) else { return }
    
    //Initialize our NIOTSHandler
    let niotsHandler = NIOTSHandler(url: url, tls: tls, group: group, groupProvider: provider, upgradePromise: upgradePromise)
    
            //We get shutdown here as soon as we are connected
    defer {
        niotsHandler.disconnect()
    }
    _ = try niotsHandler.connect()
}
```

Inside of niotsHandler we grab our clientBootstrap that contains a WebSocketHandler that handles our web socket with the HTTPInitialRequestHandler, NIOWebSocketClientUpgrader, and NIOHTTPClientUpgradeConfiguration and start the connection

    func connect() throws -> EventLoopFuture<Void> {
        let connection = try clientBootstrap()
            .connect(host: host, port: port)
            .map { channel -> () in
                self.channel = channel
            }
        connection.cascadeFailure(to: upgradePromise)
        return connection.flatMap { channel in
            return self.upgradePromise.futureResult
        }
    }
    

I call the code like so

var myniows = MyNIOWS(url: "ws://echo.websocket.org", tls: false)
 _ = try? myniows.connect()

So My question is why would the connection close as soon as I open it? Is my code faulty? Any help and suggestions is appreciated

Thanks

I recommend zooming in on the end of your code block, which you wrote as:

    //Initialize our NIOTSHandler
    let niotsHandler = NIOTSHandler(url: url, tls: tls, group: group, groupProvider: provider, upgradePromise: upgradePromise)
    
            //We get shutdown here as soon as we are connected
    defer {
        niotsHandler.disconnect()
    }
    _ = try niotsHandler.connect()

Consider the flow here. At step 1, we initialise our NIOTSHandler. Step 2 we call connect, which returns a future that you throw away. Immediately after that returns, you call niotsHandler.disconnect() in your defer block.

You need to wait for your connection attempt to succeed and then let the connection actually run, rather than just calling disconnect. NIO is asynchronous, so the call to connect does not block.

Thank you Luksa, So if I call wait() on _ = try niotsHandler.connect() and it still disconnects after waiting then would that indicate that the future failed in my connect() throws -> EvenLoopFuture<Void> {} ?

I have written code similar to this before, but I am not sure if introducing promises has thrown me off somewhere.

No, if you wait and the future failed then your wait will throw. defer is going to execute immediately after the function ends. What you need is something you can wait on until you want to disconnect.

@lukasa Ok that makes sense. So I am connecting to the Handler however I still get disconnected right away however now the upgrade is not failing after the disconnect which is progress, but still I need to keep the connection open and then upgrade, If I understand what needs to happen correctly.

I wait like this

let connect = try niotsHandler.connect()
try connect.wait()

So is it correct to assume that it is because the connection completes and finishes before the WebSocket Handler's promise actually succeeds or fails?

Here are the logs for when I connect

Connect
2021-07-24 14:38:26.946535-0500 NetworkTesting[64517:979347] [connection] nw_endpoint_handler_set_adaptive_read_handler [C1.1 174.129.224.73:80 ready socket-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, ipv6, dns)] unregister notification for read_timeout failed
2021-07-24 14:38:26.946582-0500 NetworkTesting[64517:979347] [connection] nw_endpoint_handler_set_adaptive_write_handler [C1.1 174.129.224.73:80 ready socket-flow (satisfied (Path is satisfied), viable, interface: en0, ipv4, ipv6, dns)] unregister notification for write_timeout failed
Upgraders Completion Handler______ NIO.ChannelHandlerContext
HTTP handler removed.
Disconnected from the server
Channel on upgrade______ NIOTransportServices.NIOTSConnectionChannel
Request Headers on upgrade_____ HTTPResponseHead { version: HTTP/1.1, status: switchingProtocols, headers: [("Connection", "Upgrade"), ("Date", "Sat, 24 Jul 2021 19:38:23 GMT"), ("Sec-WebSocket-Accept", "3R5vTtCdj1BU3vPdKnkx5HZVOwU="), ("Server", "Kaazing Gateway"), ("Upgrade", "websocket")] }
Terms of Service

Privacy Policy

Cookie Policy