Azure / amqpnetlite

AMQP 1.0 .NET Library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

AmqpSettings IdleTimeout error in case you time travel

optimuserik opened this issue · comments

AmqpSettings.IdleTimeout is set to int, but used as a uint

https://github.com/Azure/amqpnetlite/blob/3e42f0cc2e2981fd5c8376ef3338aa82b433eafe/src/Net/AmqpSettings.cs#L74C20-L74C31

public int IdleTimeout
        {
            get;
            set;
        }

https://github.com/Azure/amqpnetlite/blob/3e42f0cc2e2981fd5c8376ef3338aa82b433eafe/src/Connection.cs#L243C25-L243C25

IdleTimeOut = (uint)amqpSettings.IdleTimeout / 2

We may want to change the BIOS time, and if we set it to a past time this will cause issues.

When checking elapsed time

uint elapsed = (uint)((now.Ticks - last.Ticks) / Encoder.TicksPerMillisecond);

                    uint elapsed = (uint)((now.Ticks - last.Ticks) / Encoder.TicksPerMillisecond);

the substraction can go into negative, since you time traveled and it will be a very high uint number.

Since IdleTimeout is by default set to int.MaxValue

https://github.com/Azure/amqpnetlite/blob/3e42f0cc2e2981fd5c8376ef3338aa82b433eafe/src/Net/ConnectionFactoryBase.cs#L45C17-L45C28

                IdleTimeout = int.MaxValue,

which presumably is intended to mean that it 'never' times out ( which would be what we want).

However the previously mentioned

                    uint elapsed = (uint)((now.Ticks - last.Ticks) / Encoder.TicksPerMillisecond);

will still be higher than the int.MaxValue, that is the default, resulting in GetDueMiliseconds to return 0,

https://github.com/Azure/amqpnetlite/blob/3e42f0cc2e2981fd5c8376ef3338aa82b433eafe/src/Connection.cs#L962C1-L963C1

                    due = timeout > elapsed ? timeout - elapsed : 0;

which then closes the connection and throws the exception.

if (thisPtr.local > 0 &&

 if (thisPtr.local > 0 &&
                        GetDueMilliseconds(thisPtr.local, now, thisPtr.lastReceive) == 0)
                    {
                        thisPtr.connection.CloseInternal(
                            0,
                            new Error(ErrorCode.ConnectionForced)
                            {
                                Description = Fx.Format("Connection closed after idle timeout {0} ms", thisPtr.local)
                            });
                        thisPtr.SetTimerForClose();
                        return;
                    }

It would seem that the underlying issue is that AmqpSettings.IdleTimeout is an int but is used as uint.
Changing it to uint and initializing it with

                IdleTimeout = uint.MaxValue,

would fix this issue and would probably be more in line with how it is actually used.
Changing the IdleTimeout might result in a breaking change though.

commit 54554af solves the issue.
Thank you.