public async Task IoAsync()

in sources/Google.Solutions.Iap/Protocol/SshRelaySession.cs [325:420]


        public async Task<uint> IoAsync(
            Func<INetworkStream, Task<uint>> ioAction,
            Func<INetworkStream, CancellationToken, Task> resendUnacknoledgedDataAction,
            bool treatNormalCloseAsError,
            CancellationToken cancellationToken)
        {
            var attempt = 0;
            while (true)
            {
                try
                {
                    var connection = await GetConnectionAsync(
                            resendUnacknoledgedDataAction,
                            cancellationToken)
                        .ConfigureAwait(false);

                    Debug.Assert(connection != null);
                    return await ioAction(connection!).ConfigureAwait(false);
                }
                catch (WebSocketStreamClosedByClientException)
                {
                    throw;
                }
                catch (WebSocketStreamClosedByServerException e)
                {
                    IapTraceSource.Log.TraceError(e);

                    switch ((SshRelayCloseCode)e.CloseStatus)
                    {
                        case SshRelayCloseCode.NORMAL:
                        case SshRelayCloseCode.DESTINATION_READ_FAILED:
                        case SshRelayCloseCode.DESTINATION_WRITE_FAILED:
                            //
                            // NB. We get a DESTINATION_*_FAILED if the
                            // backend closed the connection (as opposed
                            // to the relay).
                            //
                            if (treatNormalCloseAsError)
                            {
                                throw;
                            }
                            else
                            {
                                //
                                // Server closed the connection normally.
                                //
                                return 0;
                            }

                        case SshRelayCloseCode.NOT_AUTHORIZED:
                            throw new SshRelayDeniedException(
                                $"The server denied access: " +
                                e.CloseStatusDescription);

                        case SshRelayCloseCode.FAILED_TO_REWIND:
                        case SshRelayCloseCode.SID_UNKNOWN:
                        case SshRelayCloseCode.SID_IN_USE:
                            throw new SshRelayReconnectException(
                                "The server closed the connection unexpectedly and " +
                                "reestablishing the connection failed: " +
                                e.CloseStatusDescription);

                        case SshRelayCloseCode.FAILED_TO_CONNECT_TO_BACKEND:
                            throw new SshRelayConnectException(
                                "The server could not connect to the backend: " +
                                e.CloseStatusDescription);

                        case SshRelayCloseCode.LOOKUP_FAILED:
                        case SshRelayCloseCode.LOOKUP_FAILED_RECONNECT:
                            throw new SshRelayBackendNotFoundException(
                                "The backend could not be found");

                        default:
                            {
                                if (attempt++ >= MaxReconnects)
                                {
                                    TraceVerbose($"Failed to reconnect after {attempt} attempts");
                                    throw;
                                }
                                else
                                {
                                    //
                                    // Try again.
                                    //
                                    await DisconnectAsync(cancellationToken)
                                        .ConfigureAwait(true);

                                    TraceVerbose("Attempting to reconnect");

                                    break;
                                }
                            }
                    }
                }
            }
        }