Core: avoid possible infinite loop if switching to same DC

People are reporting that under some circumstances, an infinite
loop could happen when TLSharp tries to handle a reconnection to
a different DC, if the DC instructed to be used is the same as
the one that was used in the last connection.

Not sure how could this happen (although the analysis present in
this github issue [1] might help understand it), but this commit
helps to make TLSharp fail fast (with an exception) instead of an
infinite loop from now on, which will help avoiding people file
issues such as [2] and [3] and instead maybe file a proper bug
report easier to understand, to try to fix the underlying root
cause.

[1] https://github.com/sochix/TLSharp/issues/719
[2] https://github.com/sochix/TLSharp/issues/803
[3] https://github.com/sochix/TLSharp/issues/839
This commit is contained in:
Andres G. Aragoneses 2019-02-10 13:53:46 +01:00
parent d04ddb1e0e
commit 60a3c62357
4 changed files with 39 additions and 11 deletions

View file

@ -0,0 +1,21 @@
namespace TLSharp.Core
{
internal class DataCenter
{
internal DataCenter (int? dcId, string address, int port)
{
DataCenterId = dcId;
Address = address;
Port = port;
}
internal DataCenter (string address, int port) : this (null, address, port)
{
}
internal int? DataCenterId { get; private set; }
internal string Address { get; private set; }
internal int Port { get; private set; }
}
}

View file

@ -59,8 +59,7 @@ namespace TLSharp.Core
private const int defaultConnectionPort = 443;
public string SessionUserId { get; set; }
public string ServerAddress { get; set; }
public int Port { get; set; }
internal DataCenter DataCenter { get; set; }
public AuthKey AuthKey { get; set; }
public ulong Id { get; set; }
public int Sequence { get; set; }
@ -89,8 +88,8 @@ namespace TLSharp.Core
writer.Write(Salt);
writer.Write(LastMessageId);
writer.Write(TimeOffset);
Serializers.String.write(writer, ServerAddress);
writer.Write(Port);
Serializers.String.write(writer, DataCenter.Address);
writer.Write(DataCenter.Port);
if (TLUser != null)
{
@ -132,6 +131,7 @@ namespace TLSharp.Core
}
var authData = Serializers.Bytes.read(reader);
var defaultDataCenter = new DataCenter (serverAddress, port);
return new Session(store)
{
@ -144,8 +144,7 @@ namespace TLSharp.Core
SessionExpires = sessionExpires,
TLUser = TLUser,
SessionUserId = sessionUserId,
ServerAddress = serverAddress,
Port = port
DataCenter = defaultDataCenter,
};
}
}
@ -157,12 +156,13 @@ namespace TLSharp.Core
public static Session TryLoadOrCreateNew(ISessionStore store, string sessionUserId)
{
var defaultDataCenter = new DataCenter (defaultConnectionAddress, defaultConnectionPort);
return store.Load(sessionUserId) ?? new Session(store)
{
Id = GenerateRandomUlong(),
SessionUserId = sessionUserId,
ServerAddress = defaultConnectionAddress,
Port = defaultConnectionPort
DataCenter = defaultDataCenter,
};
}

View file

@ -68,6 +68,7 @@
<Compile Include="Session.cs" />
<Compile Include="TelegramClient.cs" />
<Compile Include="Utils\Helpers.cs" />
<Compile Include="DataCenter.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View file

@ -45,7 +45,7 @@ namespace TLSharp.Core
_handler = handler;
_session = Session.TryLoadOrCreateNew(store, sessionUserId);
_transport = new TcpTransport(_session.ServerAddress, _session.Port, _handler);
_transport = new TcpTransport(_session.DataCenter.Address, _session.DataCenter.Port, _handler);
}
public async Task ConnectAsync(bool reconnect = false)
@ -90,10 +90,10 @@ namespace TLSharp.Core
}
var dc = dcOptions.First(d => d.Id == dcId);
var dataCenter = new DataCenter (dcId, dc.IpAddress, dc.Port);
_transport = new TcpTransport(dc.IpAddress, dc.Port, _handler);
_session.ServerAddress = dc.IpAddress;
_session.Port = dc.Port;
_session.DataCenter = dataCenter;
await ConnectAsync(true);
@ -121,6 +121,12 @@ namespace TLSharp.Core
}
catch(DataCenterMigrationException e)
{
if (_session.DataCenter.DataCenterId.HasValue &&
_session.DataCenter.DataCenterId.Value == e.DC)
{
throw new Exception($"Telegram server replied requesting a migration to DataCenter {e.DC} when this connection was already using this DataCenter", e);
}
await ReconnectToDcAsync(e.DC);
// prepare the request for another try
request.ConfirmReceived = false;