Network Framework 'connection.receive' <error> = 89

I have the following snippet of code for receiving incoming data on a NWConnection:

self.Connection.receive(minimumIncompleteLength: 1, maximumLength: self.MAX_INTAKE) {
         (data, context, isComplete, error) in

      if let err = error {
         // receive <error> returned non-nil
        self.Connection.cancel()
        return   // exit completion handler
        }
   ...
   }

This generally works and rarely receives an error. But seemingly at random, <error> will return 89. When this happens I've been sending a .cancel before returning from the completion handler.

It will work great for tens of thousands of connections, then suddenly return 89 error codes.

My question is: Should I be canceling the connection here or simply let NWFramwwork do as it will? Canceling the connection seems to throw my NGINX reverse proxy into fits, from which it never recovers without a restart.

In short what is the best practice for handling errors when receiving bytes in NWFramework?

Answered by DTS Engineer in 811322022

I’m presuming that this is TCP. There are important differences when dealing with message-oriented protocols, like UDP or QUIC.

Error 89 is ECANCELED. That’s the error I’d expect to see delivered by my receive handler when I call cancel() on a connection. As to why you’re seeing it in your situation, it’s hard to say.

It’s generally fine to redundantly cancel a connection. You may get a snarky log message, but it shouldn’t do anything bad.

what is the best practice for handling errors when receiving bytes in NWFramework?

The pattern I’ve settled on is:

func startReceive() {
    self.connection.receive(minimumIncompleteLength: 1, maximumLength: 2048) { content, _, isComplete, error in
        if let content {
            … process content …
        }
        if isComplete {
            … process EOF …
            return
        }
        if let error {
            … process error …
            return
        }
        self.startReceive()
    }
}

This design means that I process any incoming content or EOF before I check the error. And exactly what I do in the face of an error varies depending on the nature of my program. But, in general:

  • Errors coming from my side — so, my code calling cancel() — will trigger the ECANCELED, which I ignore.

  • Errors coming from the network will be returned here but also cause the connection to transition to the .failed(…) state, so I don’t need to do anything about them here.

Canceling the connection seems to throw my NGINX reverse proxy into fits, from which it never recovers without a restart.

Clearly that’s something you need to debug. The first step would be to see what’s happening on the ‘wire’. That is, what on-the-wire behaviour is causing your apoplexy in your proxy (apo-proxy, perhaps? :-).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

I’m presuming that this is TCP. There are important differences when dealing with message-oriented protocols, like UDP or QUIC.

Error 89 is ECANCELED. That’s the error I’d expect to see delivered by my receive handler when I call cancel() on a connection. As to why you’re seeing it in your situation, it’s hard to say.

It’s generally fine to redundantly cancel a connection. You may get a snarky log message, but it shouldn’t do anything bad.

what is the best practice for handling errors when receiving bytes in NWFramework?

The pattern I’ve settled on is:

func startReceive() {
    self.connection.receive(minimumIncompleteLength: 1, maximumLength: 2048) { content, _, isComplete, error in
        if let content {
            … process content …
        }
        if isComplete {
            … process EOF …
            return
        }
        if let error {
            … process error …
            return
        }
        self.startReceive()
    }
}

This design means that I process any incoming content or EOF before I check the error. And exactly what I do in the face of an error varies depending on the nature of my program. But, in general:

  • Errors coming from my side — so, my code calling cancel() — will trigger the ECANCELED, which I ignore.

  • Errors coming from the network will be returned here but also cause the connection to transition to the .failed(…) state, so I don’t need to do anything about them here.

Canceling the connection seems to throw my NGINX reverse proxy into fits, from which it never recovers without a restart.

Clearly that’s something you need to debug. The first step would be to see what’s happening on the ‘wire’. That is, what on-the-wire behaviour is causing your apoplexy in your proxy (apo-proxy, perhaps? :-).

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Thanks, Quinn. Your response was very helpful and also clarifying on how to best code the .receive completion handler!!

--Barry

Network Framework 'connection.receive' &lt;error&gt; = 89
 
 
Q