diff --git a/src/IO.Ably.Android/Platform.cs b/src/IO.Ably.Android/Platform.cs index 73ff9f369..b6bb5778e 100644 --- a/src/IO.Ably.Android/Platform.cs +++ b/src/IO.Ably.Android/Platform.cs @@ -1,29 +1,24 @@ -using IO.Ably.Transport; -using System.Net.NetworkInformation; -using IO.Ably.Push; +using System.Net.NetworkInformation; using IO.Ably.Realtime; namespace IO.Ably { internal class Platform : IPlatform { - private static readonly object _lock = new object(); - - internal static bool HookedUpToNetworkEvents { get; private set; } public Agent.PlatformRuntime PlatformId => Agent.PlatformRuntime.XamarinAndroid; - public bool SyncContextDefault => true; - public ITransportFactory TransportFactory => null; - public IMobileDevice MobileDevice { get; set; } + private static readonly object Lock = new object(); + + internal static bool HookedUpToNetworkEvents { get; set; } - public void RegisterOsNetworkStateChanged() + public void RegisterOsNetworkStateChanged(ILogger logger) { - lock (_lock) + lock (Lock) { if (HookedUpToNetworkEvents == false) { NetworkChange.NetworkAvailabilityChanged += (sender, eventArgs) => - Connection.NotifyOperatingSystemNetworkState(eventArgs.IsAvailable ? NetworkState.Online : NetworkState.Offline); + Connection.NotifyOperatingSystemNetworkState(eventArgs.IsAvailable ? NetworkState.Online : NetworkState.Offline, logger); } HookedUpToNetworkEvents = true; diff --git a/src/IO.Ably.NETFramework/Platform.cs b/src/IO.Ably.NETFramework/Platform.cs index f6765f3e6..c5e27a216 100644 --- a/src/IO.Ably.NETFramework/Platform.cs +++ b/src/IO.Ably.NETFramework/Platform.cs @@ -1,40 +1,24 @@ -using IO.Ably.Transport; -using System.Net.NetworkInformation; -using IO.Ably.Push; +using System.Net.NetworkInformation; using IO.Ably.Realtime; namespace IO.Ably { internal class Platform : IPlatform { - private static readonly object _lock = new object(); - - static Platform() - { - Initialize(); - } - - internal static bool HookedUpToNetworkEvents { get; private set; } - public Agent.PlatformRuntime PlatformId => Agent.PlatformRuntime.Framework; - public ITransportFactory TransportFactory => null; - - public IMobileDevice MobileDevice { get; set; } + private static readonly object Lock = new object(); - internal static void Initialize() - { - HookedUpToNetworkEvents = false; - } + internal static bool HookedUpToNetworkEvents { get; set; } - public void RegisterOsNetworkStateChanged() + public void RegisterOsNetworkStateChanged(ILogger logger) { - lock (_lock) + lock (Lock) { if (HookedUpToNetworkEvents == false) { NetworkChange.NetworkAvailabilityChanged += (sender, eventArgs) => - Connection.NotifyOperatingSystemNetworkState(eventArgs.IsAvailable ? NetworkState.Online : NetworkState.Offline); + Connection.NotifyOperatingSystemNetworkState(eventArgs.IsAvailable ? NetworkState.Online : NetworkState.Offline, logger); } HookedUpToNetworkEvents = true; diff --git a/src/IO.Ably.NETStandard20/Platform.cs b/src/IO.Ably.NETStandard20/Platform.cs index 72eeaa9b5..9d295c8e4 100644 --- a/src/IO.Ably.NETStandard20/Platform.cs +++ b/src/IO.Ably.NETStandard20/Platform.cs @@ -1,21 +1,10 @@ using System.Net.NetworkInformation; -using IO.Ably.Push; using IO.Ably.Realtime; -using IO.Ably.Transport; namespace IO.Ably { internal class Platform : IPlatform { - private static readonly object Lock = new object(); - - static Platform() - { - Initialize(); - } - - internal static bool HookedUpToNetworkEvents { get; private set; } - // Defined as per https://learn.microsoft.com/en-us/dotnet/standard/frameworks#preprocessor-symbols #if NET6_0 public Agent.PlatformRuntime PlatformId => Agent.PlatformRuntime.Net6; @@ -25,23 +14,18 @@ static Platform() public Agent.PlatformRuntime PlatformId => Agent.PlatformRuntime.Netstandard20; #endif - public ITransportFactory TransportFactory => null; - - public IMobileDevice MobileDevice { get; set; } + private static readonly object Lock = new object(); - internal static void Initialize() - { - HookedUpToNetworkEvents = false; - } + internal static bool HookedUpToNetworkEvents { get; set; } - public void RegisterOsNetworkStateChanged() + public void RegisterOsNetworkStateChanged(ILogger logger) { lock (Lock) { if (HookedUpToNetworkEvents == false) { NetworkChange.NetworkAvailabilityChanged += (sender, eventArgs) => - Connection.NotifyOperatingSystemNetworkState(eventArgs.IsAvailable ? NetworkState.Online : NetworkState.Offline); + Connection.NotifyOperatingSystemNetworkState(eventArgs.IsAvailable ? NetworkState.Online : NetworkState.Offline, logger); } HookedUpToNetworkEvents = true; diff --git a/src/IO.Ably.Push.Android/AndroidMobileDevice.cs b/src/IO.Ably.Push.Android/AndroidMobileDevice.cs index a96937cab..bfab35995 100644 --- a/src/IO.Ably.Push.Android/AndroidMobileDevice.cs +++ b/src/IO.Ably.Push.Android/AndroidMobileDevice.cs @@ -1,5 +1,6 @@ using System; using System.Net; +using System.Runtime.CompilerServices; using Android.App; using Android.Content; using Android.Gms.Tasks; @@ -12,13 +13,13 @@ namespace IO.Ably.Push.Android public class AndroidMobileDevice : IMobileDevice { private const string TokenType = "fcm"; - private readonly ILogger _logger; private static AblyRealtime _realtimeInstance; - internal AndroidMobileDevice(PushCallbacks callbacks, ILogger logger) + private ILogger Logger { get; set; } + + internal AndroidMobileDevice(PushCallbacks callbacks) { Callbacks = callbacks; - _logger = logger; } /// @@ -44,11 +45,11 @@ public static IRealtimeClient Initialise(ClientOptions ablyClientOptions, Action /// Initialised Ably instance which supports push notification registrations. public static IRealtimeClient Initialise(ClientOptions ablyClientOptions, PushCallbacks callbacks = null) { - var androidMobileDevice = new AndroidMobileDevice(callbacks ?? new PushCallbacks(), DefaultLogger.LoggerInstance); - IoC.MobileDevice = androidMobileDevice; + var androidMobileDevice = new AndroidMobileDevice(callbacks ?? new PushCallbacks()); // Create the instance of ably used for Push registrations _realtimeInstance = new AblyRealtime(ablyClientOptions, androidMobileDevice); + androidMobileDevice.Logger = _realtimeInstance.Logger; _realtimeInstance.Push.InitialiseStateMachine(); return _realtimeInstance; } @@ -100,15 +101,15 @@ public void RequestRegistrationToken(Action> callback) { try { - _logger.Debug("Requesting a new Registration token"); + Logger.Debug("Requesting a new Registration token"); var messagingInstance = FirebaseMessaging.Instance; var resultTask = messagingInstance.GetToken(); - resultTask.AddOnCompleteListener(new RequestTokenCompleteListener(callback, _logger)); + resultTask.AddOnCompleteListener(new RequestTokenCompleteListener(callback, Logger)); } catch (Exception e) { - _logger.Error("Error while requesting a new Registration token.", e); + Logger.Error("Error while requesting a new Registration token.", e); var errorInfo = new ErrorInfo($"Failed to request AndroidToken. Error: {e?.Message}.", 50000, HttpStatusCode.InternalServerError, e); callback(Result.Fail(errorInfo)); } diff --git a/src/IO.Ably.Push.iOS/AppleMobileDevice.cs b/src/IO.Ably.Push.iOS/AppleMobileDevice.cs index 5a37ba3f9..fe3833070 100644 --- a/src/IO.Ably.Push.iOS/AppleMobileDevice.cs +++ b/src/IO.Ably.Push.iOS/AppleMobileDevice.cs @@ -12,13 +12,13 @@ public class AppleMobileDevice : IMobileDevice { private const string TokenType = "apns"; - private readonly ILogger _logger; private static AblyRealtime _realtimeInstance; - private AppleMobileDevice(PushCallbacks callbacks, ILogger logger) + private ILogger Logger { get; set; } + + private AppleMobileDevice(PushCallbacks callbacks) { Callbacks = callbacks; - _logger = logger; } /// @@ -42,9 +42,9 @@ public static IRealtimeClient Initialise(ClientOptions ablyClientOptions, Action /// Initialised Ably instance which supports push notification registrations. public static IRealtimeClient Initialise(ClientOptions ablyClientOptions, PushCallbacks callbacks = null) { - var mobileDevice = new AppleMobileDevice(callbacks, DefaultLogger.LoggerInstance); - IoC.MobileDevice = mobileDevice; + var mobileDevice = new AppleMobileDevice(callbacks); _realtimeInstance = new AblyRealtime(ablyClientOptions, mobileDevice); + mobileDevice.Logger = _realtimeInstance.Logger; _realtimeInstance.Push.InitialiseStateMachine(); return _realtimeInstance; } @@ -95,7 +95,7 @@ public static void OnRegistrationTokenFailed(ErrorInfo error) /// public void SetPreference(string key, string value, string groupName) { - _logger.Debug($"Setting preferences: {groupName}:{key} with value {value}"); + Logger.Debug($"Setting preferences: {groupName}:{key} with value {value}"); Preferences.Set(key, value, groupName); } @@ -108,14 +108,14 @@ public string GetPreference(string key, string groupName) /// public void RemovePreference(string key, string groupName) { - _logger.Debug($"Removing preference: {groupName}:{key}"); + Logger.Debug($"Removing preference: {groupName}:{key}"); Preferences.Remove(key, groupName); } /// public void ClearPreferences(string groupName) { - _logger.Debug($"Clearing preferences group: {groupName}"); + Logger.Debug($"Clearing preferences group: {groupName}"); Preferences.Clear(groupName); } @@ -136,7 +136,7 @@ public void RequestRegistrationToken(Action> unusedAct } else { - _logger.Error($"Error signing up for remote notifications: {error.LocalizedDescription}"); + Logger.Error($"Error signing up for remote notifications: {error.LocalizedDescription}"); } }); } diff --git a/src/IO.Ably.Shared/AblyRealtime.cs b/src/IO.Ably.Shared/AblyRealtime.cs index 945a2dfe4..64c9787b3 100644 --- a/src/IO.Ably.Shared/AblyRealtime.cs +++ b/src/IO.Ably.Shared/AblyRealtime.cs @@ -19,7 +19,7 @@ public class AblyRealtime : IRealtimeClient, IDisposable { private SynchronizationContext _synchronizationContext; - internal ILogger Logger { get; set; } = DefaultLogger.LoggerInstance; + internal ILogger Logger { get; set; } internal RealtimeWorkflow Workflow { get; private set; } @@ -40,7 +40,7 @@ public AblyRealtime(string key) /// /// . public AblyRealtime(ClientOptions options) - : this(options, CreateRestFunc, IoC.MobileDevice) + : this(options, CreateRestFunc) { } @@ -51,11 +51,12 @@ internal AblyRealtime(ClientOptions options, IMobileDevice mobileDevice) internal AblyRealtime(ClientOptions options, Func createRestFunc, IMobileDevice mobileDevice = null) { - if (options.Logger != null) + if (options == null) { - Logger = options.Logger; + throw new AblyException("No clientOptions provided to AblyRealtime instance"); } + Logger = options.Logger; Logger.LogLevel = options.LogLevel; if (options.LogHandler != null) @@ -64,21 +65,22 @@ internal AblyRealtime(ClientOptions options, Func init) { Options = new ClientOptions(); init(Options); - InitializeAbly(IoC.MobileDevice); + InitializeAbly(); } /// @@ -50,11 +50,11 @@ public AblyRest(Action init) /// /// instance of clientOptions. public AblyRest(ClientOptions clientOptions) - : this(clientOptions, IoC.MobileDevice) + : this(clientOptions, null) { } - internal AblyRest(ClientOptions clientOptions, IMobileDevice mobileDevice) + internal AblyRest(ClientOptions clientOptions, IMobileDevice mobileDevice = null) { Options = clientOptions; InitializeAbly(mobileDevice); @@ -106,7 +106,7 @@ public LocalDevice Device { lock (_deviceLock) { - return LocalDevice.GetInstance(MobileDevice, Auth.ClientId); + return LocalDevice.GetInstance(MobileDevice, Auth.ClientId, Logger); } } @@ -119,22 +119,17 @@ public LocalDevice Device internal ClientOptions Options { get; } - internal ILogger Logger { get; set; } = DefaultLogger.LoggerInstance; + internal ILogger Logger { get; set; } /// Initializes the rest client and validates the passed in options. - private void InitializeAbly(IMobileDevice mobileDevice) + private void InitializeAbly(IMobileDevice mobileDevice = null) { if (Options == null) { - Logger.Error("No options provider to Ably rest"); - throw new AblyException("Invalid options"); - } - - if (Options.Logger != null) - { - Logger = Options.Logger; + throw new AblyException("No clientOptions provided to AblyRest instance"); } + Logger = Options.Logger; Logger.LogLevel = Options.LogLevel; if (Options.LogHandler != null) @@ -149,9 +144,10 @@ private void InitializeAbly(IMobileDevice mobileDevice) HttpClient = new AblyHttpClient(new AblyHttpOptions(Options)); ExecuteHttpRequest = HttpClient.Execute; AblyAuth = new AblyAuth(Options, this); - Channels = new RestChannels(this, mobileDevice); - Push = new PushRest(this, Logger); + MobileDevice = mobileDevice; + Channels = new RestChannels(this, MobileDevice); + Push = new PushRest(this, Logger); AblyAuth.OnClientIdChanged = OnAuthClientIdChanged; } diff --git a/src/IO.Ably.Shared/ClientOptions.cs b/src/IO.Ably.Shared/ClientOptions.cs index 544a2a386..500772789 100644 --- a/src/IO.Ably.Shared/ClientOptions.cs +++ b/src/IO.Ably.Shared/ClientOptions.cs @@ -432,7 +432,7 @@ internal Func NowFunc } [JsonIgnore] - internal ILogger Logger { get; set; } = DefaultLogger.LoggerInstance; + internal ILogger Logger { get; set; } = InternalLogger.Create(); internal bool SkipInternetCheck { get; set; } diff --git a/src/IO.Ably.Shared/DefaultLoggerSink.cs b/src/IO.Ably.Shared/DefaultLoggerSink.cs deleted file mode 100644 index 1667d91e3..000000000 --- a/src/IO.Ably.Shared/DefaultLoggerSink.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Diagnostics; - -namespace IO.Ably -{ - /// The default logger implementation, that writes to debug output. - internal class DefaultLoggerSink : ILoggerSink - { - private readonly object _syncRoot = new object(); - - public void LogEvent(LogLevel level, string message) - { - lock (_syncRoot) - { - Debug.WriteLine($"Ably: [{level}] {message}"); - } - } - } -} diff --git a/src/IO.Ably.Shared/Defaults.cs b/src/IO.Ably.Shared/Defaults.cs index f712d890f..cac77f496 100644 --- a/src/IO.Ably.Shared/Defaults.cs +++ b/src/IO.Ably.Shared/Defaults.cs @@ -48,7 +48,7 @@ internal static string GetVersion() public static readonly TimeSpan ConnectionStateTtl = TimeSpan.FromSeconds(120); // https://sdk.ably.com/builds/ably/specification/main/features/#DF1a public static readonly TimeSpan FallbackRetryTimeout = TimeSpan.FromMinutes(10); // https://sdk.ably.com/builds/ably/specification/main/features/#TO3l10 - public static readonly ITransportFactory WebSocketTransportFactory = IoC.TransportFactory; + public static readonly ITransportFactory WebSocketTransportFactory = new MsWebSocketTransport.TransportFactory(); internal const int TokenErrorCodesRangeStart = 40140; internal const int TokenErrorCodesRangeEnd = 40149; diff --git a/src/IO.Ably.Shared/EventEmitter.cs b/src/IO.Ably.Shared/EventEmitter.cs index 7ae0daef2..9d70aaa24 100644 --- a/src/IO.Ably.Shared/EventEmitter.cs +++ b/src/IO.Ably.Shared/EventEmitter.cs @@ -109,7 +109,7 @@ public abstract class EventEmitter : IEventEmitter { internal EventEmitter(ILogger logger) { - Logger = logger ?? DefaultLogger.LoggerInstance; + Logger = logger; } internal ILogger Logger { get; set; } diff --git a/src/IO.Ably.Shared/Http/AblyHttpClient.cs b/src/IO.Ably.Shared/Http/AblyHttpClient.cs index 05a33337c..a2c2fc856 100644 --- a/src/IO.Ably.Shared/Http/AblyHttpClient.cs +++ b/src/IO.Ably.Shared/Http/AblyHttpClient.cs @@ -17,7 +17,7 @@ internal class AblyHttpClient : IAblyHttpClient internal AblyHttpClient(AblyHttpOptions options, HttpMessageHandler messageHandler = null) { Now = options.NowFunc; - Logger = options.Logger ?? DefaultLogger.LoggerInstance; + Logger = options.Logger; Options = options; CreateInternalHttpClient(options.HttpRequestTimeout, messageHandler); SendAsync = InternalSendAsync; diff --git a/src/IO.Ably.Shared/Http/AblyHttpOptions.cs b/src/IO.Ably.Shared/Http/AblyHttpOptions.cs index 05edcb369..6dc87d2ac 100644 --- a/src/IO.Ably.Shared/Http/AblyHttpOptions.cs +++ b/src/IO.Ably.Shared/Http/AblyHttpOptions.cs @@ -56,7 +56,7 @@ public AblyHttpOptions() FallbackHostsUseDefault = false; NowFunc = Defaults.NowFunc(); - Logger = DefaultLogger.LoggerInstance; + Logger = InternalLogger.Create(); } public AblyHttpOptions(ClientOptions options) @@ -79,6 +79,11 @@ public AblyHttpOptions(ClientOptions options) NowFunc = options.NowFunc; Logger = options.Logger; + Logger.LogLevel = options.LogLevel; + if (options.LogHandler != null) + { + Logger.LoggerSink = options.LogHandler; + } } } } diff --git a/src/IO.Ably.Shared/IO.Ably.Shared.projitems b/src/IO.Ably.Shared/IO.Ably.Shared.projitems index 4b7ee91e1..2946f2e91 100644 --- a/src/IO.Ably.Shared/IO.Ably.Shared.projitems +++ b/src/IO.Ably.Shared/IO.Ably.Shared.projitems @@ -18,7 +18,6 @@ - @@ -111,7 +110,6 @@ - diff --git a/src/IO.Ably.Shared/IPlatform.cs b/src/IO.Ably.Shared/IPlatform.cs index d90983244..1e32b7a91 100644 --- a/src/IO.Ably.Shared/IPlatform.cs +++ b/src/IO.Ably.Shared/IPlatform.cs @@ -16,10 +16,6 @@ internal interface IPlatform { Agent.PlatformRuntime PlatformId { get; } - ITransportFactory TransportFactory { get; } - - IMobileDevice MobileDevice { get; set; } - /// /// This method when implemented in each Platform class includes logic to subscribe to /// NetworkStatus changes from the operating system. It is then exposed through @@ -29,6 +25,7 @@ internal interface IPlatform /// The implementation will only allow one registration to operating system network state events even /// thought this method can be called multiple times. /// - void RegisterOsNetworkStateChanged(); + /// Logger provided by the realtime client. + void RegisterOsNetworkStateChanged(ILogger logger); } } diff --git a/src/IO.Ably.Shared/InternalLogger.cs b/src/IO.Ably.Shared/InternalLogger.cs index 72ff7dbff..63fbae3cd 100644 --- a/src/IO.Ably.Shared/InternalLogger.cs +++ b/src/IO.Ably.Shared/InternalLogger.cs @@ -4,6 +4,44 @@ namespace IO.Ably { + /// Level of a log message. + public enum LogLevel : byte + { + /// + /// Verbose setting. Logs everything. + /// + Debug = 0, + + /// + /// Warning setting. Logs clues that something is not 100% right. + /// + Warning, + + /// + /// Error setting. Logs errors + /// + Error, + + /// + /// None setting. No logs produced + /// + None = 99 + } + + /// The default logger implementation, that writes to debug output. + internal class DefaultLoggerSink : ILoggerSink + { + private readonly object _syncRoot = new object(); + + public void LogEvent(LogLevel level, string message) + { + lock (_syncRoot) + { + Debug.WriteLine($"Ably: [{level}] {message}"); + } + } + } + internal class InternalLogger : IInternalLogger { private InternalLogger() diff --git a/src/IO.Ably.Shared/IoC.cs b/src/IO.Ably.Shared/IoC.cs index a16fe0f25..f7471c869 100644 --- a/src/IO.Ably.Shared/IoC.cs +++ b/src/IO.Ably.Shared/IoC.cs @@ -1,61 +1,12 @@ -using System; -using System.IO; -using System.Reflection; -using IO.Ably.Push; -using IO.Ably.Transport; - -namespace IO.Ably +namespace IO.Ably { - /// This class initializes dynamically-injected platform dependencies. - internal static class IoC + /// This class initializes Platform. + internal class IoC { - private static readonly IPlatform Platform; - - /// Load AblyPlatform.dll, instantiate AblyPlatform.PlatformImpl type. - static IoC() - { - try - { - var name = new AssemblyName("IO.Ably"); - var asm = Assembly.Load(name); - var type = asm.GetType("IO.Ably.Platform"); - if (type != null) - { - var obj = Activator.CreateInstance(type); - Platform = obj as IPlatform; - } - else - { - DefaultLogger.Debug("Platform class does not exist. Defaulting Microsoft Websocket library."); - } - } - catch (FileNotFoundException e) - { - DefaultLogger.Debug($"Assembly cannot be loaded. Defaulting Microsoft Websocket library. ({e.Message})"); - } - } - - public static ITransportFactory TransportFactory => Platform?.TransportFactory ?? new MsWebSocketTransport.TransportFactory(); - - public static void RegisterOsNetworkStateChanged() => Platform.RegisterOsNetworkStateChanged(); + public static readonly Platform Platform = new Platform(); public static Agent.PlatformRuntime PlatformId => Platform?.PlatformId ?? Agent.PlatformRuntime.Other; - public static IMobileDevice MobileDevice - { - get - { - try - { - return Platform.MobileDevice; - } - catch (Exception e) when (e is NotImplementedException) - { - DefaultLogger.Error("Mobile Device is no supported on the current platform.", e); - return null; - } - } - set => Platform.MobileDevice = value; - } + public static void RegisterOsNetworkStateChanged(ILogger logger) => Platform.RegisterOsNetworkStateChanged(logger); } } diff --git a/src/IO.Ably.Shared/MessageEncoders/CipherEncoder.cs b/src/IO.Ably.Shared/MessageEncoders/CipherEncoder.cs index 62bc875bf..9b7fae5c8 100644 --- a/src/IO.Ably.Shared/MessageEncoders/CipherEncoder.cs +++ b/src/IO.Ably.Shared/MessageEncoders/CipherEncoder.cs @@ -12,7 +12,7 @@ internal class CipherEncoder : MessageEncoder public override Result Decode(IPayload payload, DecodingContext context) { var options = context.ChannelOptions; - var logger = options?.Logger ?? DefaultLogger.LoggerInstance; + var logger = context.Logger; if (IsEmpty(payload.Data)) { diff --git a/src/IO.Ably.Shared/MessageEncoders/DecodingContext.cs b/src/IO.Ably.Shared/MessageEncoders/DecodingContext.cs index 9534e13d1..6a6dbf427 100644 --- a/src/IO.Ably.Shared/MessageEncoders/DecodingContext.cs +++ b/src/IO.Ably.Shared/MessageEncoders/DecodingContext.cs @@ -15,19 +15,15 @@ internal class DecodingContext /// /// Initializes a new instance of the class. /// - public DecodingContext() - { - ChannelOptions = new ChannelOptions(); - } - - /// - /// Initializes a new instance of the class. - /// + /// Logger passed from clientOptions. /// Channel options used for the encode / decode operations. - public DecodingContext(ChannelOptions options) + public DecodingContext(ILogger logger, ChannelOptions options = null) { + Logger = logger; ChannelOptions = options ?? new ChannelOptions(); } + + public ILogger Logger { get; set; } } /// @@ -39,10 +35,11 @@ internal static class ChannelOptionsExtensions /// Creates a new from the provided . /// /// the used in the new context. + /// logger passed from original instance. /// created with passed Channel options. - public static DecodingContext ToDecodingContext(this ChannelOptions options) + public static DecodingContext ToDecodingContext(this ChannelOptions options, ILogger logger) { - return new DecodingContext(options ?? new ChannelOptions()); + return new DecodingContext(logger, options); } } } diff --git a/src/IO.Ably.Shared/MessageEncoders/JsonEncoder.cs b/src/IO.Ably.Shared/MessageEncoders/JsonEncoder.cs index 34d9a1f8b..81b462b93 100644 --- a/src/IO.Ably.Shared/MessageEncoders/JsonEncoder.cs +++ b/src/IO.Ably.Shared/MessageEncoders/JsonEncoder.cs @@ -11,7 +11,7 @@ internal class JsonEncoder : MessageEncoder public override Result Decode(IPayload payload, DecodingContext context) { var options = context.ChannelOptions; - var logger = options?.Logger ?? DefaultLogger.LoggerInstance; + var logger = context.Logger; if (IsEmpty(payload.Data) || !CurrentEncodingIs(payload, EncodingName)) { diff --git a/src/IO.Ably.Shared/MessageEncoders/MessageHandler.cs b/src/IO.Ably.Shared/MessageEncoders/MessageHandler.cs index cf86d5925..b977101a2 100644 --- a/src/IO.Ably.Shared/MessageEncoders/MessageHandler.cs +++ b/src/IO.Ably.Shared/MessageEncoders/MessageHandler.cs @@ -125,7 +125,7 @@ private byte[] GetRequestBody(AblyRequest request) private byte[] GetMessagesRequestBody(IEnumerable payloads, ChannelOptions options) { - EncodePayloads(new DecodingContext(options), payloads); + EncodePayloads(new DecodingContext(Logger, options), payloads); #if MSGPACK if (_protocol == Protocol.MsgPack) { @@ -195,7 +195,7 @@ Type GetNullableType(Type type) } } - internal static Result DecodePayload(IMessage payload, DecodingContext context, IEnumerable encoders = null, ILogger logger = null) + internal static Result DecodePayload(IMessage payload, DecodingContext context, ILogger logger, IEnumerable encoders = null) { var actualEncoders = (encoders ?? DefaultEncoders).ToList(); var pp = context.PreviousPayload; // We take a chance that this will not be modified but replaced @@ -230,7 +230,7 @@ Result GetOriginalMessagePayload() bool isFirstEncodingBase64 = MessageEncoder.CurrentEncodingIs(payload, Base64Encoder.EncodingNameStr); if (isFirstEncodingBase64) { - var result = Base64Encoder.Decode(payload, new DecodingContext()); + var result = Base64Encoder.Decode(payload, new DecodingContext(logger)); return result.Map(x => new PayloadCache((byte[])x.Data, MessageEncoder.RemoveCurrentEncodingPart(payload))); } @@ -298,13 +298,13 @@ public PaginatedResult ParsePaginatedResponse(AblyRequest request, AblyRes if (typeof(T) == typeof(Message)) { var typedResult = result as PaginatedResult; - var context = request.ChannelOptions.ToDecodingContext(); + var context = request.ChannelOptions.ToDecodingContext(Logger); typedResult?.Items.AddRange(ParseMessagesResponse(response, context)); } else if (typeof(T) == typeof(PresenceMessage)) { var typedResult = result as PaginatedResult; - var context = request.ChannelOptions.ToDecodingContext(); + var context = request.ChannelOptions.ToDecodingContext(Logger); typedResult?.Items.AddRange(ParsePresenceMessages(response, context)); } else @@ -340,7 +340,7 @@ private static Result DecodePayloads(DecodingContext context, IEnumerable messages, ChannelOptions options) { - var context = options.ToDecodingContext(); + var context = options.ToDecodingContext(Logger); return DecodeMessages(protocolMessage, messages, context); } @@ -469,7 +469,7 @@ public Result DecodeMessages( foreach (var message in messages ?? Enumerable.Empty()) { SetMessageIdConnectionIdAndTimestamp(message, index); - var decodeResult = DecodePayload(message, context, DefaultEncoders) + var decodeResult = DecodePayload(message, context, Logger, DefaultEncoders) .IfFailure(error => Logger.Warning($"Error decoding message with id: {message.Id}. Error: {error.Message}. Exception: {error.InnerException?.Message}")); result = Result.Combine(result, decodeResult); @@ -530,30 +530,5 @@ private bool IsMsgPack() return _protocol == Protocol.MsgPack; } #endif - - internal static T FromEncoded(T encoded, ChannelOptions options = null) - where T : IMessage - { - var context = options.ToDecodingContext(); - var result = DecodePayload(encoded, context, logger: DefaultLogger.LoggerInstance); - if (result.IsFailure) - { - throw new AblyException(result.Error); - } - - return encoded; - } - - internal static T[] FromEncodedArray(T[] encodedArray, ChannelOptions options = null) - where T : IMessage - { - var context = options.ToDecodingContext(); - foreach (var encoded in encodedArray) - { - DecodePayload(encoded, context, logger: DefaultLogger.LoggerInstance); - } - - return encodedArray; - } } } diff --git a/src/IO.Ably.Shared/MessageEncoders/VCDiffEncoder.cs b/src/IO.Ably.Shared/MessageEncoders/VCDiffEncoder.cs index 0d4d568f4..e52f9665f 100644 --- a/src/IO.Ably.Shared/MessageEncoders/VCDiffEncoder.cs +++ b/src/IO.Ably.Shared/MessageEncoders/VCDiffEncoder.cs @@ -21,7 +21,7 @@ public override Result Encode(IPayload payload, DecodingContex public override Result Decode(IPayload payload, DecodingContext context) { - var logger = context.ChannelOptions?.Logger ?? DefaultLogger.LoggerInstance; + var logger = context.Logger; if (payload == null) { return Result.Ok(new ProcessedPayload()); diff --git a/src/IO.Ably.Shared/Push/LocalDevice.cs b/src/IO.Ably.Shared/Push/LocalDevice.cs index 0986f5df6..5d183811d 100644 --- a/src/IO.Ably.Shared/Push/LocalDevice.cs +++ b/src/IO.Ably.Shared/Push/LocalDevice.cs @@ -132,18 +132,18 @@ internal static void ResetDevice(IMobileDevice mobileDevice) internal static bool IsLocalDeviceInitialized => Instance != null; - internal static LocalDevice GetInstance(IMobileDevice mobileDevice, string clientId) + internal static LocalDevice GetInstance(IMobileDevice mobileDevice, string clientId, ILogger logger) { if (mobileDevice is null) { throw new AblyException( - "Cannot initialise LocalDevice instance before initialising the MobileDevice class. For Android call AndroidMobileDevice.Initialise() and for iOS call AppleMobileDevice.Initialise()"); + "Cannot initialise LocalDevice instance before initializing the MobileDevice class. For Android call AndroidMobileDevice.Initialise() and for iOS call AppleMobileDevice.Initialise()"); } switch (Instance) { case null: - if (LoadPersistedLocalDevice(mobileDevice, out var device)) + if (LoadPersistedLocalDevice(mobileDevice, out var device, logger)) { Instance = device; } @@ -159,9 +159,9 @@ internal static LocalDevice GetInstance(IMobileDevice mobileDevice, string clien } } - internal static bool LoadPersistedLocalDevice(IMobileDevice mobileDevice, out LocalDevice persistedDevice) + internal static bool LoadPersistedLocalDevice(IMobileDevice mobileDevice, out LocalDevice persistedDevice, ILogger logger) { - Debug("Loading Local Device persisted state."); + logger.Debug("Loading Local Device persisted state."); string GetDeviceSetting(string key) => mobileDevice.GetPreference(key, PersistKeys.Device.SharedName); string id = GetDeviceSetting(PersistKeys.Device.DeviceId); @@ -194,7 +194,7 @@ internal static bool LoadPersistedLocalDevice(IMobileDevice mobileDevice, out Lo } } - Debug($"LocalDevice loaded: {persistedDevice.ToJson()}"); + logger.Debug($"LocalDevice loaded: {persistedDevice.ToJson()}"); return true; } @@ -217,7 +217,5 @@ internal static void PersistRegistrationToken(IMobileDevice mobileDevice, Regist mobileDevice.SetPreference(PersistKeys.Device.TokenType, token.Type, PersistKeys.Device.SharedName); mobileDevice.SetPreference(PersistKeys.Device.Token, token.Token, PersistKeys.Device.SharedName); } - - private static void Debug(string message) => DefaultLogger.Debug($"LocalDevice: {message}"); } } diff --git a/src/IO.Ably.Shared/Realtime/ChannelAwaiter.cs b/src/IO.Ably.Shared/Realtime/ChannelAwaiter.cs index 44a707ba0..9c1360cb5 100644 --- a/src/IO.Ably.Shared/Realtime/ChannelAwaiter.cs +++ b/src/IO.Ably.Shared/Realtime/ChannelAwaiter.cs @@ -22,9 +22,9 @@ internal class ChannelAwaiter : IDisposable private readonly object _lock = new object(); private readonly Action _onTimeout; - public ChannelAwaiter(IRealtimeChannel channel, ChannelState awaitedState, ILogger logger = null, Action onTimeout = null) + public ChannelAwaiter(IRealtimeChannel channel, ChannelState awaitedState, ILogger logger, Action onTimeout = null) { - Logger = logger ?? DefaultLogger.LoggerInstance; + Logger = logger; _name = $"#{channel.Name}:{awaitedState} awaiter"; _channel = channel as RealtimeChannel; _awaitedState = awaitedState; diff --git a/src/IO.Ably.Shared/Realtime/Connection.cs b/src/IO.Ably.Shared/Realtime/Connection.cs index f2bf3f4f8..6727afc96 100644 --- a/src/IO.Ably.Shared/Realtime/Connection.cs +++ b/src/IO.Ably.Shared/Realtime/Connection.cs @@ -45,16 +45,9 @@ public sealed class Connection : EventEmitter /// The current state of the OS network connection. - public static void NotifyOperatingSystemNetworkState(NetworkState state) => - NotifyOperatingSystemNetworkState(state, null); - + /// Logger provided by the realtime client. internal static void NotifyOperatingSystemNetworkState(NetworkState state, ILogger logger) { - if (logger == null) - { - logger = DefaultLogger.LoggerInstance; - } - if (logger.IsDebug) { logger.Debug("OS Network connection state: " + state); @@ -119,8 +112,8 @@ private void CleanUpNetworkStateEvents() || State == Realtime.ConnectionState.Disconnected) && RealtimeClient.Options.QueueMessages); - internal Connection(AblyRealtime realtimeClient, Func nowFunc, ILogger logger = null) - : base(logger) + internal Connection(AblyRealtime realtimeClient, Func nowFunc) + : base(realtimeClient.Logger) { Now = nowFunc; RealtimeClient = realtimeClient; diff --git a/src/IO.Ably.Shared/Realtime/RealtimeChannel.cs b/src/IO.Ably.Shared/Realtime/RealtimeChannel.cs index d040c7eb7..6c1033679 100644 --- a/src/IO.Ably.Shared/Realtime/RealtimeChannel.cs +++ b/src/IO.Ably.Shared/Realtime/RealtimeChannel.cs @@ -126,13 +126,13 @@ internal RealtimeChannel( string name, string clientId, AblyRealtime realtimeClient, - ChannelOptions options = null, + ChannelOptions options, IMobileDevice mobileDevice = null) - : base(options?.Logger) + : base(realtimeClient.Logger) { Name = name; Options = options; - MessageDecodingContext = new DecodingContext(options); + MessageDecodingContext = new DecodingContext(Logger, options); Presence = new Presence(realtimeClient.ConnectionManager, this, clientId, Logger); RealtimeClient = realtimeClient; State = ChannelState.Initialized; diff --git a/src/IO.Ably.Shared/Realtime/Workflows/RealtimeCommands.cs b/src/IO.Ably.Shared/Realtime/Workflows/RealtimeCommands.cs index 84529ade3..3e40b99fb 100644 --- a/src/IO.Ably.Shared/Realtime/Workflows/RealtimeCommands.cs +++ b/src/IO.Ably.Shared/Realtime/Workflows/RealtimeCommands.cs @@ -378,14 +378,14 @@ private HandleConnectingTokenErrorCommand(ErrorInfo error) Error = error; } - public static RealtimeCommand Create(ErrorInfo error) + public static RealtimeCommand Create(ErrorInfo error, ILogger logger) { if (error == null || error.IsTokenError == false) { #if DEBUG throw new ArgumentException("Cannot create a TokenError command with an error that is not a token error."); #else - DefaultLogger.Warning("Cannot create a TokenError command with an error that is not a token error"); + logger.Warning("Cannot create a TokenError command with an error that is not a token error"); // TODO: Sentry alert return EmptyCommand.Instance; diff --git a/src/IO.Ably.Shared/Realtime/Workflows/RealtimeState.cs b/src/IO.Ably.Shared/Realtime/Workflows/RealtimeState.cs index a8641dcb9..29a20db80 100644 --- a/src/IO.Ably.Shared/Realtime/Workflows/RealtimeState.cs +++ b/src/IO.Ably.Shared/Realtime/Workflows/RealtimeState.cs @@ -138,20 +138,18 @@ public long IncrementSerial() public readonly List WaitingForAck = new List(); public void AddAckMessage(ProtocolMessage message, Action callback) => - WaitingForAck.Add(new MessageAndCallback(message, callback)); + WaitingForAck.Add(new MessageAndCallback(message, callback, Logger)); - public RealtimeState() - : this(null) - { - } - - public RealtimeState(List fallbackHosts, Func now = null) + public RealtimeState(List fallbackHosts, ILogger logger, Func now = null) { + Logger = logger; Connection = new ConnectionData(fallbackHosts); AttemptsInfo = new ConnectionAttemptsInfo(now); PendingMessages = new List(); } + public ILogger Logger { get; set; } + public JObject WhatDoIHave() { var stateJson = new JObject diff --git a/src/IO.Ably.Shared/Realtime/Workflows/RealtimeWorkflow.cs b/src/IO.Ably.Shared/Realtime/Workflows/RealtimeWorkflow.cs index 13572e063..d93f2ebd7 100644 --- a/src/IO.Ably.Shared/Realtime/Workflows/RealtimeWorkflow.cs +++ b/src/IO.Ably.Shared/Realtime/Workflows/RealtimeWorkflow.cs @@ -91,7 +91,7 @@ public RealtimeWorkflow(AblyRealtime client, ILogger logger) private void SetInitialConnectionState() { - var initialState = new ConnectionInitializedState(ConnectionManager, Logger); + var initialState = new ConnectionInitializedState(ConnectionManager); State.Connection.CurrentStateObject = initialState; SetRecoverKeyIfPresent(Client.Options.Recover); } @@ -430,7 +430,7 @@ async Task AttemptANewConnection() if (error.IsTokenError) { - return HandleConnectingTokenErrorCommand.Create(error) + return HandleConnectingTokenErrorCommand.Create(error, Logger) .TriggeredBy(cmd); } @@ -630,8 +630,7 @@ private void HandleConnectedCommand(SetConnectedStateCommand cmd) var connectedState = new ConnectionConnectedState( ConnectionManager, cmd.Message.Error, - cmd.IsUpdate, - Logger); + cmd.IsUpdate); SetState(connectedState); @@ -716,7 +715,7 @@ private async Task HandleSetStateCommand(RealtimeCommand comman switch (command) { case ForceStateInitializationCommand _: - var initState = new ConnectionInitializedState(ConnectionManager, Logger); + var initState = new ConnectionInitializedState(ConnectionManager); SetState(initState); break; case SetConnectedStateCommand cmd: @@ -779,7 +778,7 @@ private async Task HandleSetStateCommand(RealtimeCommand comman ClearAckQueueAndFailMessages(ErrorInfo.ReasonFailed); var error = TransformIfTokenErrorAndNotRetryable(); - var failedState = new ConnectionFailedState(ConnectionManager, error, Logger); + var failedState = new ConnectionFailedState(ConnectionManager, error); SetState(failedState); ConnectionManager.DestroyTransport(); @@ -886,7 +885,7 @@ ErrorInfo TransformIfTokenErrorAndNotRetryable() State.Connection.ClearKeyAndId(); ClearAckQueueAndFailMessages(ErrorInfo.ReasonClosed); - var closedState = new ConnectionClosedState(ConnectionManager, cmd.Error, Logger) + var closedState = new ConnectionClosedState(ConnectionManager, cmd.Error) { Exception = cmd.Exception, }; @@ -1017,7 +1016,7 @@ public void QueueAck(ProtocolMessage message, Action callback) { if (message.AckRequired) { - State.WaitingForAck.Add(new MessageAndCallback(message, callback)); + State.WaitingForAck.Add(new MessageAndCallback(message, callback, Logger)); if (Logger.IsDebug) { Logger.Debug($"Message ({message.Action}) with serial ({message.MsgSerial}) was queued to get Ack"); diff --git a/src/IO.Ably.Shared/Rest/ChannelOptions.cs b/src/IO.Ably.Shared/Rest/ChannelOptions.cs index c80106879..5063a6da3 100644 --- a/src/IO.Ably.Shared/Rest/ChannelOptions.cs +++ b/src/IO.Ably.Shared/Rest/ChannelOptions.cs @@ -3,15 +3,13 @@ namespace IO.Ably { /// - /// Channel options used for initialising channels. + /// Channel options used for initializing channels. /// public class ChannelOptions { private ChannelModes _modes = new ChannelModes(); private ChannelParams _params = new ChannelParams(); - internal ILogger Logger { get; set; } - /// /// Indicates whether the channel is encrypted. /// @@ -48,7 +46,7 @@ public ChannelModes Modes /// /// . public ChannelOptions(CipherParams @params) - : this(DefaultLogger.LoggerInstance, true, @params) { } + : this(true, @params) { } /// /// Initializes a new instance of the class. @@ -57,21 +55,12 @@ public ChannelOptions(CipherParams @params) /// optional, . /// optional, . /// optional, . - public ChannelOptions( - bool encrypted = false, - CipherParams @params = null, - ChannelModes modes = null, - ChannelParams channelParams = null) - : this(null, encrypted, @params, modes, channelParams) { } - internal ChannelOptions( - ILogger logger, bool encrypted = false, CipherParams @params = null, ChannelModes modes = null, ChannelParams channelParams = null) { - Logger = logger ?? DefaultLogger.LoggerInstance; Encrypted = encrypted; CipherParams = @params ?? Crypto.GetDefaultParams(); Modes = modes; @@ -84,7 +73,6 @@ internal ChannelOptions( /// cipher key. public ChannelOptions(byte[] key) { - Logger = DefaultLogger.LoggerInstance; Encrypted = true; CipherParams = new CipherParams(key); } diff --git a/src/IO.Ably.Shared/Transport/ConnectionManager.cs b/src/IO.Ably.Shared/Transport/ConnectionManager.cs index 5daef36a0..f208e5279 100644 --- a/src/IO.Ably.Shared/Transport/ConnectionManager.cs +++ b/src/IO.Ably.Shared/Transport/ConnectionManager.cs @@ -42,7 +42,7 @@ private ITransportFactory GetTransportFactory() public ConnectionManager(Connection connection, Func nowFunc, ILogger logger) { Now = nowFunc; - Logger = logger ?? DefaultLogger.LoggerInstance; + Logger = logger; Connection = connection; } @@ -220,7 +220,7 @@ public void Send( return; } - Result encodingResult = Handler.EncodeProtocolMessage(message, channelOptions.ToDecodingContext()); + Result encodingResult = Handler.EncodeProtocolMessage(message, channelOptions.ToDecodingContext(Logger)); if (encodingResult.IsFailure) { Logger.Error($"Failed to encode protocol message: {encodingResult.Error.Message}"); diff --git a/src/IO.Ably.Shared/Transport/MessageAndCallback.cs b/src/IO.Ably.Shared/Transport/MessageAndCallback.cs index d3f58081e..6837778bf 100644 --- a/src/IO.Ably.Shared/Transport/MessageAndCallback.cs +++ b/src/IO.Ably.Shared/Transport/MessageAndCallback.cs @@ -13,11 +13,11 @@ internal class MessageAndCallback public Action Callback { get; } - public MessageAndCallback(ProtocolMessage message, Action callback, ILogger logger = null) + public MessageAndCallback(ProtocolMessage message, Action callback, ILogger logger) { Message = message; Callback = callback; - Logger = logger ?? DefaultLogger.LoggerInstance; + Logger = logger; } protected bool Equals(MessageAndCallback other) diff --git a/src/IO.Ably.Shared/Transport/MsWebSocketConnection.cs b/src/IO.Ably.Shared/Transport/MsWebSocketConnection.cs index 3768a716e..1dc70654f 100644 --- a/src/IO.Ably.Shared/Transport/MsWebSocketConnection.cs +++ b/src/IO.Ably.Shared/Transport/MsWebSocketConnection.cs @@ -50,7 +50,7 @@ public enum ConnectionState private bool _disposed; - internal ILogger Logger { get; set; } = DefaultLogger.LoggerInstance; + internal ILogger Logger { get; set; } private readonly Uri _uri; private Action _handler; diff --git a/src/IO.Ably.Shared/Transport/MsWebSocketTransport.cs b/src/IO.Ably.Shared/Transport/MsWebSocketTransport.cs index 26a09cd88..c0da6efdb 100644 --- a/src/IO.Ably.Shared/Transport/MsWebSocketTransport.cs +++ b/src/IO.Ably.Shared/Transport/MsWebSocketTransport.cs @@ -83,7 +83,7 @@ protected MsWebSocketTransport(TransportParams parameters, MsWebSocketOptions so BinaryProtocol = parameters.UseBinaryProtocol; WebSocketUri = parameters.GetUri(); - Logger = parameters.Logger ?? DefaultLogger.LoggerInstance; + Logger = parameters.Logger; } /// diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionClosedState.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionClosedState.cs index 294666153..38f71b66c 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionClosedState.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionClosedState.cs @@ -7,13 +7,13 @@ internal class ConnectionClosedState : ConnectionStateBase { public override ErrorInfo DefaultErrorInfo => ErrorInfo.ReasonClosed; - public ConnectionClosedState(IConnectionContext context, ILogger logger) - : this(context, null, logger) + public ConnectionClosedState(IConnectionContext context) + : this(context, null) { } - public ConnectionClosedState(IConnectionContext context, ErrorInfo error, ILogger logger) - : base(context, logger) + public ConnectionClosedState(IConnectionContext context, ErrorInfo error) + : base(context) { Error = error ?? ErrorInfo.ReasonClosed; } diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionClosingState.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionClosingState.cs index 7a7a854ab..418f0196b 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionClosingState.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionClosingState.cs @@ -15,12 +15,12 @@ internal class ConnectionClosingState : ConnectionStateBase private readonly ICountdownTimer _timer; public ConnectionClosingState(IConnectionContext context, bool connectedTransport, ILogger logger) - : this(context, null, connectedTransport, new CountdownTimer("Closing state timer", logger), logger) + : this(context, null, connectedTransport, new CountdownTimer("Closing state timer", logger)) { } - public ConnectionClosingState(IConnectionContext context, ErrorInfo error, bool connectedTransport, ICountdownTimer timer, ILogger logger) - : base(context, logger) + public ConnectionClosingState(IConnectionContext context, ErrorInfo error, bool connectedTransport, ICountdownTimer timer) + : base(context) { _connectedTransport = connectedTransport; _timer = timer; diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionConnectedState.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionConnectedState.cs index c61f182f2..7afe1af15 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionConnectedState.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionConnectedState.cs @@ -10,9 +10,8 @@ internal class ConnectionConnectedState : ConnectionStateBase public ConnectionConnectedState( IConnectionContext context, ErrorInfo error = null, - bool isUpdate = false, - ILogger logger = null) - : base(context, logger) + bool isUpdate = false) + : base(context) { Error = error; IsUpdate = isUpdate; diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionConnectingState.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionConnectingState.cs index d5c3ce993..0eccd1073 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionConnectingState.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionConnectingState.cs @@ -12,12 +12,12 @@ internal class ConnectionConnectingState : ConnectionStateBase private readonly ICountdownTimer _timer; public ConnectionConnectingState(IConnectionContext context, ILogger logger) - : this(context, new CountdownTimer("Connecting state timer", logger), logger) + : this(context, new CountdownTimer("Connecting state timer", logger)) { } - public ConnectionConnectingState(IConnectionContext context, ICountdownTimer timer, ILogger logger) - : base(context, logger) + public ConnectionConnectingState(IConnectionContext context, ICountdownTimer timer) + : base(context) { _timer = timer; } diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionDisconnectedState.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionDisconnectedState.cs index a8d4bd624..9dcce5412 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionDisconnectedState.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionDisconnectedState.cs @@ -10,12 +10,12 @@ internal class ConnectionDisconnectedState : ConnectionStateBase private readonly ICountdownTimer _timer; public ConnectionDisconnectedState(IConnectionContext context, ErrorInfo error, ILogger logger) - : this(context, error, new CountdownTimer("Disconnected state timer", logger), logger) + : this(context, error, new CountdownTimer("Disconnected state timer", logger)) { } - public ConnectionDisconnectedState(IConnectionContext context, ErrorInfo error, ICountdownTimer timer, ILogger logger) - : base(context, logger) + public ConnectionDisconnectedState(IConnectionContext context, ErrorInfo error, ICountdownTimer timer) + : base(context) { _timer = timer; Error = error; diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionFailedState.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionFailedState.cs index fcacdf4c9..39225ce5d 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionFailedState.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionFailedState.cs @@ -7,8 +7,8 @@ internal class ConnectionFailedState : ConnectionStateBase { public override ErrorInfo DefaultErrorInfo => ErrorInfo.ReasonFailed; - public ConnectionFailedState(IConnectionContext context, ErrorInfo error, ILogger logger) - : base(context, logger) + public ConnectionFailedState(IConnectionContext context, ErrorInfo error) + : base(context) { Error = error ?? ErrorInfo.ReasonFailed; } diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionInitializedState.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionInitializedState.cs index b14c135c0..2effae7d4 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionInitializedState.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionInitializedState.cs @@ -5,8 +5,8 @@ namespace IO.Ably.Transport.States.Connection { internal class ConnectionInitializedState : ConnectionStateBase { - public ConnectionInitializedState(IConnectionContext context, ILogger logger) - : base(context, logger) + public ConnectionInitializedState(IConnectionContext context) + : base(context) { } public override bool CanQueue => true; diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionStateBase.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionStateBase.cs index 5c1b0a612..d29e250ca 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionStateBase.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionStateBase.cs @@ -10,14 +10,11 @@ namespace IO.Ably.Transport.States.Connection [DebuggerDisplay("{State}")] internal abstract class ConnectionStateBase { - protected ConnectionStateBase(IConnectionContext context, ILogger logger) + protected ConnectionStateBase(IConnectionContext context) { - Logger = logger ?? DefaultLogger.LoggerInstance; Context = context; } - internal ILogger Logger { get; private set; } - protected readonly IConnectionContext Context; public abstract ConnectionState State { get; } diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionSuspendedState.cs b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionSuspendedState.cs index 7bb87afdc..ede283e08 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ConnectionSuspendedState.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ConnectionSuspendedState.cs @@ -10,17 +10,17 @@ internal class ConnectionSuspendedState : ConnectionStateBase public override ErrorInfo DefaultErrorInfo => ErrorInfo.ReasonSuspended; public ConnectionSuspendedState(IConnectionContext context, ILogger logger) - : this(context, null, new CountdownTimer("Suspended state timer", logger), logger) + : this(context, null, new CountdownTimer("Suspended state timer", logger)) { } public ConnectionSuspendedState(IConnectionContext context, ErrorInfo error, ILogger logger) - : this(context, error, new CountdownTimer("Suspended state timer", logger), logger) + : this(context, error, new CountdownTimer("Suspended state timer", logger)) { } - public ConnectionSuspendedState(IConnectionContext context, ErrorInfo error, ICountdownTimer timer, ILogger logger) - : base(context, logger) + public ConnectionSuspendedState(IConnectionContext context, ErrorInfo error, ICountdownTimer timer) + : base(context) { _timer = timer; Error = error ?? ErrorInfo.ReasonSuspended; diff --git a/src/IO.Ably.Shared/Transport/States/Connection/ICountdownTimer.cs b/src/IO.Ably.Shared/Transport/States/Connection/ICountdownTimer.cs index 3af5e53fd..50877aae7 100644 --- a/src/IO.Ably.Shared/Transport/States/Connection/ICountdownTimer.cs +++ b/src/IO.Ably.Shared/Transport/States/Connection/ICountdownTimer.cs @@ -35,7 +35,7 @@ internal class CountdownTimer : ICountdownTimer, IDisposable public CountdownTimer(string name, ILogger logger) { - _logger = logger ?? DefaultLogger.LoggerInstance; + _logger = logger; _name = name; _lock = new object(); } diff --git a/src/IO.Ably.Shared/Transport/TransportParams.cs b/src/IO.Ably.Shared/Transport/TransportParams.cs index e43eecf1a..b44803669 100644 --- a/src/IO.Ably.Shared/Transport/TransportParams.cs +++ b/src/IO.Ably.Shared/Transport/TransportParams.cs @@ -87,7 +87,7 @@ private TransportParams() { } - internal static async Task Create(string host, AblyAuth auth, ClientOptions options, string connectionKey = null, long? connectionSerial = null, ILogger logger = null) + internal static async Task Create(string host, AblyAuth auth, ClientOptions options, string connectionKey = null, long? connectionSerial = null) { var result = new TransportParams { @@ -101,8 +101,8 @@ internal static async Task Create(string host, AblyAuth auth, C FallbackHosts = options.GetFallbackHosts(), UseBinaryProtocol = options.UseBinaryProtocol, RecoverValue = options.Recover, - Logger = logger ?? options.Logger, - AdditionalParameters = StringifyParameters(options.TransportParams), + Logger = auth.Logger, + AdditionalParameters = StringifyParameters(options.TransportParams, auth.Logger), AuthMethod = auth.AuthMethod, Agents = options.Agents }; @@ -124,7 +124,7 @@ internal static async Task Create(string host, AblyAuth auth, C return result; - Dictionary StringifyParameters(Dictionary originalParams) + Dictionary StringifyParameters(Dictionary originalParams, ILogger logger) { if (originalParams is null) { @@ -148,7 +148,7 @@ string ConvertValue(string key, object value) } catch (Exception e) { - logger?.Error($"Error converting custom transport parameter '{key}'. Error: {e.Message}"); + logger.Error($"Error converting custom transport parameter '{key}'. Error: {e.Message}"); return string.Empty; } diff --git a/src/IO.Ably.Shared/Types/Message.cs b/src/IO.Ably.Shared/Types/Message.cs index 2ff2ce9fb..3a41eade0 100644 --- a/src/IO.Ably.Shared/Types/Message.cs +++ b/src/IO.Ably.Shared/Types/Message.cs @@ -155,69 +155,5 @@ public override int GetHashCode() return hashCode; } } - - /// - /// Decodes the current message data using the default list of encoders. - /// - /// encoded message object. - /// optional channel options. . - /// message with decoded payload. - public static Message FromEncoded(Message encoded, ChannelOptions options = null) - { - return MessageHandler.FromEncoded(encoded, options); - } - - /// - /// Decodes an array of messages. . - /// - /// array of encoded Messages. - /// optional channel options. . - /// array of decoded messages. - public static Message[] FromEncodedArray(Message[] encoded, ChannelOptions options = null) - { - return MessageHandler.FromEncodedArray(encoded, options); - } - - /// - /// Decodes the json representation of a Message using the default list of encoders. - /// - /// json representation of a Message. - /// optional channel options. . - /// message with decoded payload. - /// AblyException if there is an issue decoding the message. The most likely error is invalid json string. - public static Message FromEncoded(string messageJson, ChannelOptions options = null) - { - try - { - var message = JsonHelper.Deserialize(messageJson); - return FromEncoded(message, options); - } - catch (Exception e) - { - DefaultLogger.Error($"Error decoding message: {messageJson}", e); - throw new AblyException("Error decoding message. Error: " + e.Message, ErrorCodes.InternalError); - } - } - - /// - /// Decodes a json representation of an array of messages using the default list of encoders. - /// - /// json representation of an array of messages. - /// optional channel options. . - /// array of decoded messages. - /// AblyException if there is an issue decoding the message. The most likely error is invalid json string. - public static Message[] FromEncodedArray(string messagesJson, ChannelOptions options = null) - { - try - { - var messages = JsonHelper.Deserialize>(messagesJson).ToArray(); - return FromEncodedArray(messages, options); - } - catch (Exception e) - { - DefaultLogger.Error($"Error decoding message: {messagesJson}", e); - throw new AblyException("Error decoding messages. Error: " + e.Message, ErrorCodes.InternalError); - } - } } } diff --git a/src/IO.Ably.Shared/Utils/ConnectionChangeAwaiter.cs b/src/IO.Ably.Shared/Utils/ConnectionChangeAwaiter.cs index f6ab118a7..8a8eafc4c 100644 --- a/src/IO.Ably.Shared/Utils/ConnectionChangeAwaiter.cs +++ b/src/IO.Ably.Shared/Utils/ConnectionChangeAwaiter.cs @@ -7,7 +7,7 @@ namespace IO.Ably.Utils internal class ConnectionChangeAwaiter { private ConnectionState _currentState; - + private ILogger _logger; private readonly Connection _connection; private readonly string _id = Guid.NewGuid().ToString("D").Split('-')[0]; @@ -15,13 +15,14 @@ public ConnectionChangeAwaiter(Connection connection) { _connection = connection; _currentState = _connection.State; + _logger = connection.Logger; } public async Task<(bool, ConnectionState?)> Wait(TimeSpan timeout) { - if (DefaultLogger.IsDebug) + if (_logger.IsDebug) { - DefaultLogger.Debug($"[{_id}] Waiting for state change for {timeout.TotalSeconds} seconds"); + _logger.Debug($"[{_id}] Waiting for state change for {timeout.TotalSeconds} seconds"); } TaskCompletionSource<(bool, ConnectionState?)> taskCompletionSource = diff --git a/src/IO.Ably.Tests.Shared/Extensions/MessageExtensions.cs b/src/IO.Ably.Tests.Shared/Extensions/MessageExtensions.cs new file mode 100644 index 000000000..8b6d63f0f --- /dev/null +++ b/src/IO.Ably.Tests.Shared/Extensions/MessageExtensions.cs @@ -0,0 +1,100 @@ +using IO.Ably.MessageEncoders; +using System; +using System.Collections.Generic; +using System.Text; +using IO.Ably.Tests.Shared.Helpers; + +namespace IO.Ably.Tests.Shared.Extensions +{ + internal static class MessageExtensions + { + /// + /// Decodes the json representation of a Message using the default list of encoders. + /// + /// json representation of a Message. + /// optional channel options. . + /// message with decoded payload. + /// AblyException if there is an issue decoding the message. The most likely error is invalid json string. + public static Message FromEncoded(string messageJson, ChannelOptions options = null) + { + try + { + var message = JsonHelper.Deserialize(messageJson); + return FromEncoded(message, options); + } + catch (Exception e) + { + DefaultLogger.Error($"Error decoding message: {messageJson}", e); + throw new AblyException("Error decoding message. Error: " + e.Message, ErrorCodes.InternalError); + } + } + + /// + /// Decodes a json representation of an array of messages using the default list of encoders. + /// + /// json representation of an array of messages. + /// optional channel options. . + /// array of decoded messages. + /// AblyException if there is an issue decoding the message. The most likely error is invalid json string. + public static Message[] FromEncodedArray(string messagesJson, ChannelOptions options = null) + { + try + { + var messages = JsonHelper.Deserialize>(messagesJson).ToArray(); + return FromEncodedArray(messages, options); + } + catch (Exception e) + { + DefaultLogger.Error($"Error decoding message: {messagesJson}", e); + throw new AblyException("Error decoding messages. Error: " + e.Message, ErrorCodes.InternalError); + } + } + + /// + /// Decodes the current message data using the default list of encoders. + /// + /// encoded message object. + /// optional channel options. . + /// message with decoded payload. + public static Message FromEncoded(Message encoded, ChannelOptions options = null) + { + return FromEncodedHandler(encoded, options); + } + + /// + /// Decodes an array of messages. . + /// + /// array of encoded Messages. + /// optional channel options. . + /// array of decoded messages. + public static Message[] FromEncodedArray(Message[] encoded, ChannelOptions options = null) + { + return FromEncodedArrayHandler(encoded, options); + } + + internal static T FromEncodedHandler(T encoded, ChannelOptions options = null) + where T : IMessage + { + var context = options.ToDecodingContext(DefaultLogger.LoggerInstance); + var result = MessageHandler.DecodePayload(encoded, context, logger: DefaultLogger.LoggerInstance); + if (result.IsFailure) + { + throw new AblyException(result.Error); + } + + return encoded; + } + + internal static T[] FromEncodedArrayHandler(T[] encodedArray, ChannelOptions options = null) + where T : IMessage + { + var context = options.ToDecodingContext(DefaultLogger.LoggerInstance); + foreach (var encoded in encodedArray) + { + MessageHandler.DecodePayload(encoded, context, logger: DefaultLogger.LoggerInstance); + } + + return encodedArray; + } + } +} diff --git a/src/IO.Ably.Shared/DefaultLogger.cs b/src/IO.Ably.Tests.Shared/Helpers/DefaultLogger.cs similarity index 72% rename from src/IO.Ably.Shared/DefaultLogger.cs rename to src/IO.Ably.Tests.Shared/Helpers/DefaultLogger.cs index 4179dd226..dd68deda8 100644 --- a/src/IO.Ably.Shared/DefaultLogger.cs +++ b/src/IO.Ably.Tests.Shared/Helpers/DefaultLogger.cs @@ -1,31 +1,7 @@ using System; -namespace IO.Ably +namespace IO.Ably.Tests.Shared.Helpers { - /// Level of a log message. - public enum LogLevel : byte - { - /// - /// Verbose setting. Logs everything. - /// - Debug = 0, - - /// - /// Warning setting. Logs clues that something is not 100% right. - /// - Warning, - - /// - /// Error setting. Logs errors - /// - Error, - - /// - /// None setting. No logs produced - /// - None = 99 - } - /// An utility class for logging various messages. public static class DefaultLogger { @@ -73,13 +49,6 @@ public static ILoggerSink LoggerSink /// public static bool IsDebug => LoggerInstance.LogLevel == LogLevel.Debug; - internal static IDisposable SetTempDestination(ILoggerSink loggerSink) - { - ILoggerSink oldLoggerSink = LoggerInstance.LoggerSink; - LoggerInstance.LoggerSink = loggerSink; - return new ActionOnDispose(() => LoggerInstance.LoggerSink = oldLoggerSink); - } - /// Log an error message. internal static void Error(string message, Exception ex) { diff --git a/src/IO.Ably.Tests.Shared/IO.Ably.Tests.Shared.projitems b/src/IO.Ably.Tests.Shared/IO.Ably.Tests.Shared.projitems index 69a5c73b1..33d96a3af 100644 --- a/src/IO.Ably.Tests.Shared/IO.Ably.Tests.Shared.projitems +++ b/src/IO.Ably.Tests.Shared/IO.Ably.Tests.Shared.projitems @@ -31,6 +31,8 @@ + + diff --git a/src/IO.Ably.Tests.Shared/Infrastructure/AblyRealtimeSpecs.cs b/src/IO.Ably.Tests.Shared/Infrastructure/AblyRealtimeSpecs.cs index 0037d5296..b6c560fc9 100644 --- a/src/IO.Ably.Tests.Shared/Infrastructure/AblyRealtimeSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Infrastructure/AblyRealtimeSpecs.cs @@ -6,6 +6,7 @@ using IO.Ably.Push; using IO.Ably.Realtime; using IO.Ably.Tests.Realtime; +using IO.Ably.Tests.Shared.Helpers; using IO.Ably.Types; using Xunit.Abstractions; diff --git a/src/IO.Ably.Tests.Shared/Infrastructure/AblySpecs.cs b/src/IO.Ably.Tests.Shared/Infrastructure/AblySpecs.cs index 991bc3ccb..4eae12e46 100644 --- a/src/IO.Ably.Tests.Shared/Infrastructure/AblySpecs.cs +++ b/src/IO.Ably.Tests.Shared/Infrastructure/AblySpecs.cs @@ -1,4 +1,5 @@ using System; +using IO.Ably.Tests.Shared.Helpers; using Xunit.Abstractions; namespace IO.Ably.Tests diff --git a/src/IO.Ably.Tests.Shared/Infrastructure/ConnectionAwaiter.cs b/src/IO.Ably.Tests.Shared/Infrastructure/ConnectionAwaiter.cs index bb80eccc3..bf04991ea 100644 --- a/src/IO.Ably.Tests.Shared/Infrastructure/ConnectionAwaiter.cs +++ b/src/IO.Ably.Tests.Shared/Infrastructure/ConnectionAwaiter.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Threading.Tasks; using IO.Ably.Realtime; +using IO.Ably.Tests.Shared.Helpers; namespace IO.Ably.Tests.Infrastructure { diff --git a/src/IO.Ably.Tests.Shared/Infrastructure/FakeConnectionContext.cs b/src/IO.Ably.Tests.Shared/Infrastructure/FakeConnectionContext.cs index d90e696dc..2cf641246 100644 --- a/src/IO.Ably.Tests.Shared/Infrastructure/FakeConnectionContext.cs +++ b/src/IO.Ably.Tests.Shared/Infrastructure/FakeConnectionContext.cs @@ -13,7 +13,7 @@ internal class FakeConnectionContext : IConnectionContext { public FakeConnectionContext() { - Connection = new Connection(null, TestHelpers.NowFunc()); + Connection = new Connection(new AblyRealtime(new ClientOptions() { Key = "dummy", AutoConnect = false }), TestHelpers.NowFunc()); Connection.Initialise(); } diff --git a/src/IO.Ably.Tests.Shared/Infrastructure/FakeTransport.cs b/src/IO.Ably.Tests.Shared/Infrastructure/FakeTransport.cs index 0a98c0533..1956fe2ae 100644 --- a/src/IO.Ably.Tests.Shared/Infrastructure/FakeTransport.cs +++ b/src/IO.Ably.Tests.Shared/Infrastructure/FakeTransport.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using IO.Ably.Realtime; +using IO.Ably.Tests.Shared.Helpers; using IO.Ably.Transport; using IO.Ably.Types; diff --git a/src/IO.Ably.Tests.Shared/Infrastructure/TestTransportFactory.cs b/src/IO.Ably.Tests.Shared/Infrastructure/TestTransportFactory.cs index 9485b5dcc..7bb87bca9 100644 --- a/src/IO.Ably.Tests.Shared/Infrastructure/TestTransportFactory.cs +++ b/src/IO.Ably.Tests.Shared/Infrastructure/TestTransportFactory.cs @@ -25,7 +25,7 @@ internal TestTransportFactory(Action onWrappedTransportCre public ITransport CreateTransport(TransportParams parameters) { - var factory = IoC.TransportFactory; + var factory = Defaults.WebSocketTransportFactory; var transport = new TestTransportWrapper(factory.CreateTransport(parameters), parameters.UseBinaryProtocol ? Defaults.Protocol : Protocol.Json); diff --git a/src/IO.Ably.Tests.Shared/Infrastructure/TestTransportWrapper.cs b/src/IO.Ably.Tests.Shared/Infrastructure/TestTransportWrapper.cs index 30954183b..7f55443ff 100644 --- a/src/IO.Ably.Tests.Shared/Infrastructure/TestTransportWrapper.cs +++ b/src/IO.Ably.Tests.Shared/Infrastructure/TestTransportWrapper.cs @@ -3,6 +3,7 @@ using System.Net.Sockets; using IO.Ably.MessageEncoders; using IO.Ably.Realtime; +using IO.Ably.Tests.Shared.Helpers; using IO.Ably.Transport; using IO.Ably.Types; diff --git a/src/IO.Ably.Tests.Shared/LoggerTests.cs b/src/IO.Ably.Tests.Shared/LoggerTests.cs index 43ad4c117..fae3fe8df 100644 --- a/src/IO.Ably.Tests.Shared/LoggerTests.cs +++ b/src/IO.Ably.Tests.Shared/LoggerTests.cs @@ -1,5 +1,6 @@ using System; using FluentAssertions; +using IO.Ably.Tests.Shared.Helpers; using Xunit; namespace IO.Ably.AcceptanceTests @@ -56,10 +57,10 @@ public void TestLogger() } [Fact] - public void ClientOptionsWithNoLoggerSpecified_ShouldUseTheDefaultLogger() + public void ClientOptionsWithNoLoggerSpecified_ShouldUseTheInternalLogger() { var opts = new ClientOptions(); - Assert.Same(opts.Logger, DefaultLogger.LoggerInstance); + Assert.IsType(opts.Logger); } [Fact] diff --git a/src/IO.Ably.Tests.Shared/MessageEncodes/Base64EncoderTests.cs b/src/IO.Ably.Tests.Shared/MessageEncodes/Base64EncoderTests.cs index 583f18eb5..5329c0a66 100644 --- a/src/IO.Ably.Tests.Shared/MessageEncodes/Base64EncoderTests.cs +++ b/src/IO.Ably.Tests.Shared/MessageEncodes/Base64EncoderTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using IO.Ably.MessageEncoders; +using IO.Ably.Tests.Shared.Helpers; using Xunit; namespace IO.Ably.Tests.MessageEncodes @@ -26,7 +27,7 @@ public void WithBase64EncodedPayload_ConvertsItBackToBinaryData() { IPayload payload = new Message { Data = _base64Data, Encoding = "base64" }; - payload = _encoder.Decode(payload, new DecodingContext()).Value; + payload = _encoder.Decode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; ((byte[])payload.Data).Should().BeEquivalentTo(_binaryData); payload.Encoding.Should().BeEmpty(); @@ -37,7 +38,7 @@ public void WithBase64EncodingBeforeOtherEncodings_ConvertsDataAndStripsEncoding { IPayload payload = new Message { Data = _base64Data, Encoding = "utf-8/base64" }; - payload = _encoder.Decode(payload, new DecodingContext()).Value; + payload = _encoder.Decode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; ((byte[])payload.Data).Should().BeEquivalentTo(_binaryData); payload.Encoding.Should().Be("utf-8"); @@ -48,7 +49,7 @@ public void WithMessageAnotherEncoding_LeavesDataAndEncodingIntact() { IPayload payload = new Message { Data = _stringData, Encoding = "utf-8" }; - payload = _encoder.Decode(payload, new DecodingContext()).Value; + payload = _encoder.Decode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; payload.Data.Should().Be(_stringData); payload.Encoding.Should().Be("utf-8"); @@ -68,7 +69,7 @@ public void WithBinaryData_LeavesDataAndEncodingIntact() #pragma warning disable 162 IPayload payload = new Message { Data = _binaryData }; - payload = _encoder.Encode(payload, new DecodingContext()).Value; + payload = _encoder.Encode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; payload.Data.Should().Be(_binaryData); payload.Encoding.Should().BeNull(); @@ -83,7 +84,7 @@ public void WithBinaryPayloadWithoutPriorEncoding_ConvertsDataToBase64StringAndS { IPayload payload = new Message { Data = _binaryData }; - payload = _encoder.Encode(payload, new DecodingContext()).Value; + payload = _encoder.Encode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; payload.Data.Should().Be(_base64Data); payload.Encoding.Should().Be(_encoder.EncodingName); @@ -94,7 +95,7 @@ public void WithBinaryPayloadAndExistingEncoding_ConvertsDataToBase64StringAndAd { IPayload payload = new Message { Data = _binaryData, Encoding = "cipher" }; - payload = _encoder.Encode(payload, new DecodingContext()).Value; + payload = _encoder.Encode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; payload.Data.Should().Be(_base64Data); payload.Encoding.Should().Be("cipher/" + _encoder.EncodingName); @@ -105,7 +106,7 @@ public void WithStringPayload_LeavesDataAndEncodingIntact() { IPayload payload = new Message { Data = _stringData }; - payload = _encoder.Encode(payload, new DecodingContext()).Value; + payload = _encoder.Encode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; payload.Data.Should().Be(_stringData); payload.Encoding.Should().BeNull(); @@ -116,7 +117,7 @@ public void WithEmptyPayload_LeavesDataAndEncodingIntact() { IPayload payload = new Message(); - payload = _encoder.Encode(payload, new DecodingContext()).Value; + payload = _encoder.Encode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; payload.Data.Should().BeNull(); payload.Encoding.Should().BeNull(); diff --git a/src/IO.Ably.Tests.Shared/MessageEncodes/CipherEncoderTests.cs b/src/IO.Ably.Tests.Shared/MessageEncodes/CipherEncoderTests.cs index c27746be3..44d16bf93 100644 --- a/src/IO.Ably.Tests.Shared/MessageEncodes/CipherEncoderTests.cs +++ b/src/IO.Ably.Tests.Shared/MessageEncodes/CipherEncoderTests.cs @@ -2,6 +2,7 @@ using FluentAssertions; using IO.Ably.Encryption; using IO.Ably.MessageEncoders; +using IO.Ably.Tests.Shared.Helpers; using Xunit; namespace IO.Ably.Tests.MessageEncodes @@ -48,7 +49,7 @@ public void WithInvalidKeyLength_Throws() var encoder = new CipherEncoder(); var error = Assert.Throws(delegate { - encoder.Encode(new Message { Data = "string" }, options.ToDecodingContext()); + encoder.Encode(new Message { Data = "string" }, options.ToDecodingContext(DefaultLogger.LoggerInstance)); }); error.InnerException.Should().BeOfType(); @@ -61,7 +62,7 @@ public void WithInvalidKey_Throws() var encoder = new CipherEncoder(); var error = Assert.Throws(delegate { - encoder.Encode(new Message { Data = "string" }, options.ToDecodingContext()); + encoder.Encode(new Message { Data = "string" }, options.ToDecodingContext(DefaultLogger.LoggerInstance)); }); error.InnerException.Should().BeOfType(); @@ -81,7 +82,7 @@ public void WithInvalidAlgorithm_Throws() var encoder = new CipherEncoder(); var error = Assert.Throws(() => { - encoder.Encode(new Message { Data = "string" }, options.ToDecodingContext()); + encoder.Encode(new Message { Data = "string" }, options.ToDecodingContext(DefaultLogger.LoggerInstance)); }); error.Message.Should().Contain("Currently only the AES encryption algorithm is supported"); @@ -100,7 +101,7 @@ public void WithStringData_EncryptsDataAndSetsCorrectEncoding() { IPayload payload = new Message { Data = _stringData }; - payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; var result = _crypto.Decrypt(payload.Data as byte[]).GetText(); @@ -123,7 +124,7 @@ public void WithStringData_EncryptsTheDataAndAddsEncodingAndExtraUtf8() { IPayload payload = new Message { Data = _stringData }; - payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; string result = _crypto.Decrypt((byte[])payload.Data).GetText(); result.Should().Be(_stringData); @@ -135,7 +136,7 @@ public void WithBinaryData_EncryptsTheDataAndAddsCorrectEncoding() { IPayload payload = new Message { Data = _binaryData }; - payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; byte[] result = _crypto.Decrypt((byte[])payload.Data); result.Should().BeEquivalentTo(_binaryData); @@ -147,7 +148,7 @@ public void WithJsonData_EncryptsTheDataAndAddsCorrectEncodings() { IPayload payload = new Message { Data = _stringData, Encoding = "json" }; - payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; string result = _crypto.Decrypt((byte[])payload.Data).GetText(); result.Should().BeEquivalentTo(_stringData); @@ -159,7 +160,7 @@ public void WithAlreadyEncryptedData_LeavesDataAndEncodingIntact() { IPayload payload = new Message { Data = _encryptedData, Encoding = "utf-8/cipher+aes-256-cbc" }; - payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Encode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; payload.Data.Should().BeSameAs(_encryptedData); payload.Encoding.Should().Be("utf-8/cipher+aes-256-cbc"); @@ -178,7 +179,7 @@ public void WithCipherPayload_DecryptsDataAndStripsEncoding() { IPayload payload = new Message { Data = _encryptedBinaryData, Encoding = "cipher+aes-256-cbc" }; - payload = _encoder.Decode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Decode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; ((byte[])payload.Data).Should().BeEquivalentTo(_binaryData); payload.Encoding.Should().BeEmpty(); @@ -189,7 +190,7 @@ public void WithCipherPayloadBeforeOtherPayloads_DecryptsDataAndStripsCipherEnco { IPayload payload = new Message { Data = _encryptedBinaryData, Encoding = "utf-8/cipher+aes-256-cbc" }; - payload = _encoder.Decode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Decode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; ((byte[])payload.Data).Should().BeEquivalentTo(_binaryData); payload.Encoding.Should().Be("utf-8"); @@ -200,7 +201,7 @@ public void WithOtherTypeOfPayload_LeavesDataAndEncodingIntact() { IPayload payload = new Message { Data = "test", Encoding = "utf-8" }; - payload = _encoder.Decode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Decode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; payload.Data.Should().Be("test"); payload.Encoding.Should().Be("utf-8"); @@ -213,7 +214,7 @@ public void WithCipherEncodingThatDoesNotMatchTheCurrentCipher_LeavesMessageUnen const string encryptedValue = "test"; IPayload payload = new Message { Data = encryptedValue, Encoding = initialEncoding }; - var result = _encoder.Decode(payload, _channelOptions.ToDecodingContext()); + var result = _encoder.Decode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)); result.IsFailure.Should().BeTrue(); payload.Encoding.Should().Be(initialEncoding); @@ -233,7 +234,7 @@ public void WithCipherPayload_DecryptsDataAndStripsEncoding() { IPayload payload = new Message { Data = _encryptedBinaryData, Encoding = "cipher+aes-256-cbc" }; - payload = _encoder.Decode(payload, _channelOptions.ToDecodingContext()).Value; + payload = _encoder.Decode(payload, _channelOptions.ToDecodingContext(DefaultLogger.LoggerInstance)).Value; ((byte[])payload.Data).Should().BeEquivalentTo(_binaryData); payload.Encoding.Should().BeEmpty(); diff --git a/src/IO.Ably.Tests.Shared/MessageEncodes/JsonEncoderTests.cs b/src/IO.Ably.Tests.Shared/MessageEncodes/JsonEncoderTests.cs index 37911cb74..1ac51c761 100644 --- a/src/IO.Ably.Tests.Shared/MessageEncodes/JsonEncoderTests.cs +++ b/src/IO.Ably.Tests.Shared/MessageEncodes/JsonEncoderTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using IO.Ably.MessageEncoders; +using IO.Ably.Tests.Shared.Helpers; using Newtonsoft.Json.Linq; using Xunit; @@ -24,13 +25,13 @@ protected JsonEncoderTests() private IPayload EncodePayload(object data, string encoding = "") { var payload = new Message { Data = data, Encoding = encoding }; - return _encoder.Encode(payload, new DecodingContext()).Value; + return _encoder.Encode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; } private IPayload DecodePayload(object data, string encoding = "") { var payload = new Message { Data = data, Encoding = encoding }; - return _encoder.Decode(payload, new DecodingContext()).Value; + return _encoder.Decode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; } public class Decode : JsonEncoderTests @@ -69,7 +70,7 @@ public void WithAnotherPayload_LeavesDataAndEncoding() [Fact] public void WithInvalidJsonPayload_ShouldReturnFailedResult() { - var result = _encoder.Decode(new Message { Data = "test", Encoding = "json" }, new DecodingContext()); + var result = _encoder.Decode(new Message { Data = "test", Encoding = "json" }, new DecodingContext(DefaultLogger.LoggerInstance)); result.IsFailure.Should().BeTrue(); result.Error.Message.Should().Be("Invalid Json data: 'test'"); } diff --git a/src/IO.Ably.Tests.Shared/MessageEncodes/MessageDecodingAcceptanceTests.cs b/src/IO.Ably.Tests.Shared/MessageEncodes/MessageDecodingAcceptanceTests.cs index eb692cf64..578225793 100644 --- a/src/IO.Ably.Tests.Shared/MessageEncodes/MessageDecodingAcceptanceTests.cs +++ b/src/IO.Ably.Tests.Shared/MessageEncodes/MessageDecodingAcceptanceTests.cs @@ -3,6 +3,7 @@ using IO.Ably.Encryption; using IO.Ably.MessageEncoders; using IO.Ably.Tests; +using IO.Ably.Tests.Shared.Helpers; using Xunit; using Xunit.Abstractions; @@ -28,8 +29,9 @@ public void WithBase64EncodingBeforeOtherEncodings_SavesDecodedDataToTheContext( { var payload = new Message { Data = _base64Data, Encoding = "utf-8/base64" }; - var context = new DecodingContext(); - MessageHandler.DecodePayload(payload, context); + var logger = DefaultLogger.LoggerInstance; + var context = new DecodingContext(logger); + MessageHandler.DecodePayload(payload, context, logger); context.PreviousPayload.GetBytes().Should().BeEquivalentTo(_binaryData); context.PreviousPayload.Encoding.Should().BeEquivalentTo("utf-8"); @@ -39,12 +41,14 @@ public void WithBase64EncodingBeforeOtherEncodings_SavesDecodedDataToTheContext( public void WhenBase64IsNotTheFirstEncoding_ShouldSaveTheOriginalPayloadInContext() { var message = new Message { Data = new { Text = "Hello" } }; - MessageHandler.EncodePayload(message, new DecodingContext()); + + var logger = DefaultLogger.LoggerInstance; + MessageHandler.EncodePayload(message, new DecodingContext(logger)); var payloadData = message.Data as string; var payloadEncoding = message.Encoding; - var context = new DecodingContext(); - MessageHandler.DecodePayload(message, context); + var context = new DecodingContext(logger); + MessageHandler.DecodePayload(message, context, logger); context.PreviousPayload.GetBytes().Should().BeEquivalentTo(payloadData.GetBytes()); context.PreviousPayload.Encoding.Should().Be(payloadEncoding); } @@ -62,8 +66,10 @@ public void WithFailedEncoding_ShouldLeaveOriginalDataAndEncodingInPayload() GenerateKey(Crypto.DefaultKeylength), Encryption.CipherMode.CBC)); - var context = channelOptions.ToDecodingContext(); - var result = MessageHandler.DecodePayload(payload, context); + var logger = DefaultLogger.LoggerInstance; + + var context = channelOptions.ToDecodingContext(logger); + var result = MessageHandler.DecodePayload(payload, context, logger); result.IsFailure.Should().BeTrue(); payload.Encoding.Should().Be(initialEncoding); diff --git a/src/IO.Ably.Tests.Shared/MessageEncodes/Utf8EncoderTests.cs b/src/IO.Ably.Tests.Shared/MessageEncodes/Utf8EncoderTests.cs index bb9d4c9e6..52af4592c 100644 --- a/src/IO.Ably.Tests.Shared/MessageEncodes/Utf8EncoderTests.cs +++ b/src/IO.Ably.Tests.Shared/MessageEncodes/Utf8EncoderTests.cs @@ -1,5 +1,6 @@ using FluentAssertions; using IO.Ably.MessageEncoders; +using IO.Ably.Tests.Shared.Helpers; using Xunit; namespace IO.Ably.Tests.MessageEncodes @@ -20,7 +21,7 @@ protected Utf8EncoderTests() private IPayload DecodePayload(object data, string encoding = "") { var payload = new Message { Data = data, Encoding = encoding }; - return _encoder.Decode(payload, new DecodingContext()).Value; + return _encoder.Decode(payload, new DecodingContext(DefaultLogger.LoggerInstance)).Value; } public class Decode : Utf8EncoderTests diff --git a/src/IO.Ably.Tests.Shared/Push/LocalDeviceTests.cs b/src/IO.Ably.Tests.Shared/Push/LocalDeviceTests.cs index eecb7af3f..c640ef514 100644 --- a/src/IO.Ably.Tests.Shared/Push/LocalDeviceTests.cs +++ b/src/IO.Ably.Tests.Shared/Push/LocalDeviceTests.cs @@ -6,6 +6,7 @@ using IO.Ably.Realtime; using IO.Ably.Tests.Infrastructure; using IO.Ably.Tests.Realtime; +using IO.Ably.Tests.Shared.Helpers; using IO.Ably.Types; using Newtonsoft.Json.Linq; using Xunit; @@ -65,7 +66,7 @@ public async Task LoadPersistedLocalDevice_ShouldLoadAllSavedProperties() const string token = "registration_token"; SetSetting(PersistKeys.Device.Token, token); - var loadResult = LocalDevice.LoadPersistedLocalDevice(mobileDevice, out var localDevice); + var loadResult = LocalDevice.LoadPersistedLocalDevice(mobileDevice, out var localDevice, DefaultLogger.LoggerInstance); loadResult.Should().BeTrue(); localDevice.Platform.Should().Be(mobileDevice.DevicePlatform); localDevice.FormFactor.Should().Be(mobileDevice.FormFactor); @@ -131,7 +132,7 @@ public void WhenPlatformsDoesNotSupportPushNotifications_DeviceShouldBeNull() rest.MobileDevice.Should().BeNull(); } - [Fact(Skip = "Intermittently fails")] + [Fact] [Trait("spec", "RSH8a")] public void LocalDevice_IsOnlyInitialisedOnce() { @@ -156,7 +157,7 @@ public void LocalDevice_WhenInitialised_ShouldHave_CorrectProperties_set() device.FormFactor.Should().NotBeEmpty(); } - [Fact(Skip = "Intermittently fails")] + [Fact] [Trait("spec", "RSH8a")] [Trait("spec", "RSH8b")] public void LocalDevice_WhenRestClientContainsClientId_ShouldHaveTheSameClientId() diff --git a/src/IO.Ably.Tests.Shared/Realtime/ChannelSandboxSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ChannelSandboxSpecs.cs index 5aa8c564b..1484b6140 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ChannelSandboxSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ChannelSandboxSpecs.cs @@ -856,7 +856,7 @@ public async Task WhenSubscribingToAChannelWithInsufficientPermissions_ShouldSet // do nothing }); - var result = await new ChannelAwaiter(channel, ChannelState.Failed).WaitAsync(); + var result = await new ChannelAwaiter(channel, ChannelState.Failed, client.Logger).WaitAsync(); await Task.Delay(100); result.IsSuccess.Should().BeTrue(); @@ -1017,7 +1017,7 @@ public async Task WhenAttachingToAChannelFromMultipleThreads_ItShouldNotThrowAnE await Task.WhenAll(task, task2); - var result = await new ChannelAwaiter(channel, ChannelState.Attached).WaitAsync(); + var result = await new ChannelAwaiter(channel, ChannelState.Attached, client1.Logger).WaitAsync(); result.IsSuccess.Should().BeTrue(); } diff --git a/src/IO.Ably.Tests.Shared/Realtime/ChannelSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ChannelSpecs.cs index 8b1ace3af..e40a4c615 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ChannelSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ChannelSpecs.cs @@ -15,6 +15,7 @@ using FluentAssertions; using FluentAssertions.Execution; +using IO.Ably.Tests.Shared.Helpers; using Xunit; using Xunit.Abstractions; @@ -486,7 +487,7 @@ public async Task WhenConnectionIsClosedClosingSuspendedOrFailed_ShouldCallCallb var client = await GetConnectedClient(); // Closed - client.Workflow.SetState(new ConnectionClosedState(client.ConnectionManager, Logger)); + client.Workflow.SetState(new ConnectionClosedState(client.ConnectionManager)); var closedAttach = await client.Channels.Get("closed").AttachAsync(); AssertAttachResultIsFailure(closedAttach); @@ -503,8 +504,7 @@ public async Task WhenConnectionIsClosedClosingSuspendedOrFailed_ShouldCallCallb // Failed client.Workflow.SetState(new ConnectionFailedState( client.ConnectionManager, - ErrorInfo.ReasonFailed, - Logger)); + ErrorInfo.ReasonFailed)); var failedAttach = await client.Channels.Get("failed").AttachAsync(); AssertAttachResultIsFailure(failedAttach); @@ -534,7 +534,7 @@ public async Task WhenConnectionIsClosedClosingSuspendedOrFailed_ShouldThrowErro switch (state) { case ConnectionState.Closed: - client.Workflow.SetState(new ConnectionClosedState(client.ConnectionManager, Logger)); + client.Workflow.SetState(new ConnectionClosedState(client.ConnectionManager)); break; case ConnectionState.Closing: client.Workflow.SetState(new ConnectionClosingState(client.ConnectionManager, false, Logger)); @@ -545,8 +545,7 @@ public async Task WhenConnectionIsClosedClosingSuspendedOrFailed_ShouldThrowErro case ConnectionState.Failed: client.Workflow.SetState(new ConnectionFailedState( client.ConnectionManager, - ErrorInfo.ReasonFailed, - Logger)); + ErrorInfo.ReasonFailed)); break; } @@ -1111,7 +1110,7 @@ public async Task SendsChannelParamsWithAttachMessage() var channel = await GetTestChannel(client, channelOptions); channel.Attach(); - channel.WaitForState(ChannelState.Attaching); + await channel.WaitForState(ChannelState.Attaching); await client.ProcessCommands(); var protocolMessage = LastCreatedTransport.LastMessageSend; @@ -1130,7 +1129,7 @@ public async Task SendsChannelModesWithAttachMessage() var channel = await GetTestChannel(client, channelOptions); channel.Attach(); - channel.WaitForState(ChannelState.Attaching); + await channel.WaitForState(ChannelState.Attaching); await client.ProcessCommands(); var protocolMessage = LastCreatedTransport.LastMessageSend; @@ -1279,7 +1278,7 @@ public async Task WithAMessageThatFailDecryption_ShouldDeliverMessageAndNotPutAn }; var message = new Message("name", "encrypted with otherChannelOptions"); - MessageHandler.EncodePayloads(otherChannelOptions.ToDecodingContext(), new[] { message }); + MessageHandler.EncodePayloads(otherChannelOptions.ToDecodingContext(client.Logger), new[] { message }); client.FakeMessageReceived(message, encryptedChannel.Name); @@ -1302,14 +1301,16 @@ public async Task channel.Subscribe(msg => { receivedMessage = msg; }); var message = new Message("name", "encrypted with otherChannelOptions") { Encoding = "json" }; - MessageHandler.EncodePayloads(otherChannelOptions.ToDecodingContext(), new[] { message }); + MessageHandler.EncodePayloads(otherChannelOptions.ToDecodingContext(client.Logger), new[] { message }); var testSink = new TestLoggerSink(); - using (DefaultLogger.SetTempDestination(testSink)) - { - client.FakeMessageReceived(message, channel.Name); - await client.ProcessCommands(); - } + var oldSink = client.Logger.LoggerSink; + client.Logger.LoggerSink = testSink; + + client.FakeMessageReceived(message, channel.Name); + await client.ProcessCommands(); + + client.Logger.LoggerSink = oldSink; receivedMessage.Encoding.Should().Be(message.Encoding); testSink.Messages.Should().Contain(x => x.Contains("Error decrypting payload")); @@ -1587,7 +1588,7 @@ public async Task WithUntilAttach_ShouldPassAttachedSerialToHistoryQuery() var client = await GetConnectedClient(); var channel = client.Channels.Get("history"); - client.ProcessMessage(new ProtocolMessage(ProtocolMessage.MessageAction.Attached) + await client.ProcessMessage(new ProtocolMessage(ProtocolMessage.MessageAction.Attached) { Channel = "history", ChannelSerial = "101" diff --git a/src/IO.Ably.Tests.Shared/Realtime/ChannelsSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ChannelsSpecs.cs index 722feb98b..9c8a45522 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ChannelsSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ChannelsSpecs.cs @@ -188,7 +188,7 @@ public async Task ReleaseAll_ShouldRemoveChannelWhenDetached() // Act client.FakeProtocolMessageReceived(new ProtocolMessage(ProtocolMessage.MessageAction.Detached, TestChannelName)); - await new ChannelAwaiter(channel, ChannelState.Detached).WaitAsync(); + await new ChannelAwaiter(channel, ChannelState.Detached, client.Logger).WaitAsync(); // Assert client.Channels.Should().BeEmpty(); diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionAttemptsInfoSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionAttemptsInfoSpecs.cs index 02f7d8e38..fe8220080 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionAttemptsInfoSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionAttemptsInfoSpecs.cs @@ -4,6 +4,7 @@ using FluentAssertions; using IO.Ably.Realtime; using IO.Ably.Realtime.Workflow; +using IO.Ably.Tests.Shared.Helpers; using IO.Ably.Transport; using Xunit; using Xunit.Abstractions; @@ -12,7 +13,7 @@ namespace IO.Ably.Tests.Realtime { public class ConnectionAttemptsInfoSpecs : MockHttpRealtimeSpecs { - private readonly RealtimeState _state = new RealtimeState(); + private readonly RealtimeState _state = new RealtimeState(null, DefaultLogger.LoggerInstance); private ConnectionAttemptsInfo Info => _state.AttemptsInfo; @@ -46,7 +47,7 @@ public void ShouldSuspend_WhenFirstAttemptIsWithinConnectionStateTtl_ShouldRetur public void ShouldSuspend_WhenFirstAttemptEqualOrGreaterThanConnectionStateTtl_ShouldReturnTrue() { var now = new Now(); - var state = new RealtimeState(null, now.ValueFn); + var state = new RealtimeState(null, DefaultLogger.LoggerInstance, now.ValueFn); state.AttemptsInfo.Attempts.Add(new ConnectionAttempt(now.Value)); diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionSpecs/ConnectionFailureSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionSpecs/ConnectionFailureSpecs.cs index 417f0a58f..d8e8a99b3 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionSpecs/ConnectionFailureSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionSpecs/ConnectionFailureSpecs.cs @@ -302,12 +302,13 @@ public async Task WhenInDisconnectedState_ReconnectUsingIncrementalBackoffTimeou // Upper bound = min((retryAttempt + 2) / 3, 2) * initialTimeout // Lower bound = 0.8 * Upper bound - disconnectedRetryTimeouts[0].Should().BeInRange(4, 5); - disconnectedRetryTimeouts[1].Should().BeInRange(5.33, 6.66); - disconnectedRetryTimeouts[2].Should().BeInRange(6.66, 8.33); + const double calculationDelayTimeout = 0.15; + disconnectedRetryTimeouts[0].Should().BeInRange(4, 5 + calculationDelayTimeout); + disconnectedRetryTimeouts[1].Should().BeInRange(5.33, 6.66 + calculationDelayTimeout); + disconnectedRetryTimeouts[2].Should().BeInRange(6.66, 8.33 + calculationDelayTimeout); for (var i = 3; i < disconnectedRetryTimeouts.Count; i++) { - disconnectedRetryTimeouts[i].Should().BeInRange(8, 10); + disconnectedRetryTimeouts[i].Should().BeInRange(8, 10 + calculationDelayTimeout); } } diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionSpecs/EventEmitterSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionSpecs/EventEmitterSpecs.cs index 6abe4bbeb..48c140d5b 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionSpecs/EventEmitterSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionSpecs/EventEmitterSpecs.cs @@ -5,6 +5,7 @@ using FluentAssertions; using IO.Ably.Realtime; using IO.Ably.Tests.Infrastructure; +using IO.Ably.Tests.Shared.Helpers; using IO.Ably.Types; using Xunit; using Xunit.Abstractions; diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ClosedStateSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ClosedStateSpecs.cs index 89ebc2751..12fa9f6e6 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ClosedStateSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ClosedStateSpecs.cs @@ -13,16 +13,12 @@ namespace IO.Ably.Tests public class ClosedStateSpecs : AblySpecs { private readonly ConnectionClosedState _state; - private readonly IInternalLogger _logger; public ClosedStateSpecs(ITestOutputHelper output) : base(output) { - var sink = new TestLoggerSink(); - - _logger = InternalLogger.Create(Defaults.DefaultLogLevel, sink); var context = new FakeConnectionContext(); - _state = new ConnectionClosedState(context, _logger); + _state = new ConnectionClosedState(context); } [Fact] @@ -45,7 +41,7 @@ public void WhenConnectCalled_MovesToConnectingState() public void WhenCloseCalled_ShouldDoNothing() { // Act - new ConnectionClosedState(null, _logger).Close(); + new ConnectionClosedState(null).Close(); } [Theory] diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ClosingStateSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ClosingStateSpecs.cs index 1ca7d1139..6efe7a12b 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ClosingStateSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ClosingStateSpecs.cs @@ -144,7 +144,7 @@ public async Task OnErrorReceived_TimerIsAbortedAndStateIsFailedState() private ConnectionClosingState GetState(ErrorInfo info = null, bool connectedTransport = true) { - return new ConnectionClosingState(_context, info, connectedTransport, _timer, Logger); + return new ConnectionClosingState(_context, info, connectedTransport, _timer); } } } diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ConnectingStateSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ConnectingStateSpecs.cs index be4f96354..e8581191b 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ConnectingStateSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/ConnectingStateSpecs.cs @@ -20,7 +20,7 @@ public ConnectingStateSpecs(ITestOutputHelper output) { _context = new FakeConnectionContext(); _timer = new FakeTimer(); - _state = new ConnectionConnectingState(_context, _timer, Logger); + _state = new ConnectionConnectingState(_context, _timer); } [Fact] diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/DisconnectedStateSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/DisconnectedStateSpecs.cs index 5bd5e8ae9..a86764dcd 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/DisconnectedStateSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/DisconnectedStateSpecs.cs @@ -107,7 +107,7 @@ public async Task AfterAnInterval_ShouldRetryConnection() private ConnectionDisconnectedState GetState(ErrorInfo error = null) { - return new ConnectionDisconnectedState(_context, error, _timer, Logger); + return new ConnectionDisconnectedState(_context, error, _timer); } } } diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/FailedStateSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/FailedStateSpecs.cs index fe57496e2..8efc821a1 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/FailedStateSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/FailedStateSpecs.cs @@ -74,7 +74,7 @@ public async Task ShouldNotHandleInboundMessages(ProtocolMessage.MessageAction a private ConnectionFailedState GetState(ErrorInfo info = null) { - return new ConnectionFailedState(_context, info, Logger); + return new ConnectionFailedState(_context, info); } } } diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/InitializedStateSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/InitializedStateSpecs.cs index 403aed238..bf13b693d 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/InitializedStateSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/InitializedStateSpecs.cs @@ -17,7 +17,7 @@ public InitializedStateSpecs(ITestOutputHelper output) : base(output) { var context = new FakeConnectionContext(); - _state = new ConnectionInitializedState(context, Logger); + _state = new ConnectionInitializedState(context); } [Fact] diff --git a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/SuspendedStateSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/SuspendedStateSpecs.cs index eb1544981..9bc9e895c 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/SuspendedStateSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/ConnectionStateTests/SuspendedStateSpecs.cs @@ -20,7 +20,7 @@ public class SuspendedStateSpecs : AblySpecs private ConnectionSuspendedState GetState(ErrorInfo info = null) { - return new ConnectionSuspendedState(_context, info, _timer, Logger); + return new ConnectionSuspendedState(_context, info, _timer); } public SuspendedStateSpecs(ITestOutputHelper output) diff --git a/src/IO.Ably.Tests.Shared/Realtime/DeltaSandboxSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/DeltaSandboxSpecs.cs index b3d13cb70..e18abb32b 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/DeltaSandboxSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/DeltaSandboxSpecs.cs @@ -231,7 +231,7 @@ public async Task WhenDeltaDecodeFail_ShouldSetStateToAttachingLogTheErrorAndDis var firstMessageReceived = new TaskCompletionAwaiter(); using (((IInternalLogger)Logger).CreateDisposableLoggingContext(testSink)) { - var realtime = await GetRealtimeClient(protocol); + var realtime = await GetRealtimeClient(protocol, (options, _) => options.LogHandler = testSink); var channel = realtime.Channels.Get(channelName, new ChannelOptions(channelParams: new ChannelParams { { "delta", "vcdiff" } })); var received = new List(); diff --git a/src/IO.Ably.Tests.Shared/Realtime/RealtimeSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/RealtimeSpecs.cs index 9760c9288..046305d9c 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/RealtimeSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/RealtimeSpecs.cs @@ -137,9 +137,7 @@ public void AutomaticNetworkDetectionCanBeDisabledByClientOption(bool enabled) { // Because this test depends on static state in the 'Platform' type we need // to (re)Initialize the static 'Platform' state before each test run. - - Platform.Initialize(); - + Platform.HookedUpToNetworkEvents = false; _ = new AblyRealtime(new ClientOptions(ValidKey) { AutomaticNetworkStateMonitoring = enabled, diff --git a/src/IO.Ably.Tests.Shared/Realtime/RealtimeWorkflowSpecs.cs b/src/IO.Ably.Tests.Shared/Realtime/RealtimeWorkflowSpecs.cs index 88e859eb8..a58ae34ef 100644 --- a/src/IO.Ably.Tests.Shared/Realtime/RealtimeWorkflowSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Realtime/RealtimeWorkflowSpecs.cs @@ -180,7 +180,7 @@ public async Task OnAttached_ClearsAckQueue() { var client = GetDisconnectedClient(); - client.State.WaitingForAck.Add(new MessageAndCallback(new ProtocolMessage(), null)); + client.State.WaitingForAck.Add(new MessageAndCallback(new ProtocolMessage(), null, client.Logger)); client.ExecuteCommand(SetSuspendedStateCommand.Create(null)); @@ -267,7 +267,7 @@ public async Task ShouldClearsAckQueue() { var client = GetDisconnectedClient(); - client.State.WaitingForAck.Add(new MessageAndCallback(new ProtocolMessage(), null)); + client.State.WaitingForAck.Add(new MessageAndCallback(new ProtocolMessage(), null, client.Logger)); client.ExecuteCommand(SetClosedStateCommand.Create()); @@ -307,7 +307,7 @@ public async Task OnAttached_ClearsAckQueue() { var client = GetDisconnectedClient(); - client.State.WaitingForAck.Add(new MessageAndCallback(new ProtocolMessage(), null)); + client.State.WaitingForAck.Add(new MessageAndCallback(new ProtocolMessage(), null, client.Logger)); client.ExecuteCommand(SetClosedStateCommand.Create()); diff --git a/src/IO.Ably.Tests.Shared/Rest/ChannelSandboxSpecs.cs b/src/IO.Ably.Tests.Shared/Rest/ChannelSandboxSpecs.cs index c7462eacc..0f0132c4e 100644 --- a/src/IO.Ably.Tests.Shared/Rest/ChannelSandboxSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Rest/ChannelSandboxSpecs.cs @@ -514,7 +514,6 @@ public async Task WithEncryptionCipherMismatch_ShouldLeaveMessageEncryptedAndLog }); var opts = GetOptions(_examples); - opts.Logger = logger; var channel1 = client.Channels.Get("persisted:encryption", opts); const string payload = "test payload"; @@ -522,7 +521,7 @@ public async Task WithEncryptionCipherMismatch_ShouldLeaveMessageEncryptedAndLog await Task.Delay(1000); - var channel2 = client.Channels.Get("persisted:encryption", new ChannelOptions(logger, true)); + var channel2 = client.Channels.Get("persisted:encryption", new ChannelOptions(true)); var message = (await channel2.HistoryAsync()).Items.First(); @@ -635,15 +634,14 @@ public async Task WithTestMessagePayloadsWhenDecoding_ShouldEncodeMessagesAsPerS public async Task WithEncryptionCipherAlgorithmMismatch_ShouldLeaveMessageEncryptedAndLogError(Protocol protocol) { var loggerSink = new TestLoggerSink(); - var logger = InternalLogger.Create(Defaults.DefaultLogLevel, loggerSink); - var client = await GetRestClient(protocol); + var client = await GetRestClient(protocol, options => options.LogHandler = loggerSink); var channel1 = client.Channels.Get("persisted:encryption", GetOptions(_examples)); const string payload = "test payload"; await channel1.PublishAsync("test", payload); - var channel2 = client.Channels.Get("persisted:encryption", new ChannelOptions(logger, true, new CipherParams(Crypto.GenerateRandomKey(128, CipherMode.CBC)))); + var channel2 = client.Channels.Get("persisted:encryption", new ChannelOptions(true, new CipherParams(Crypto.GenerateRandomKey(128, CipherMode.CBC)))); await Task.Delay(1000); diff --git a/src/IO.Ably.Tests.Shared/Rest/RestSpecs.cs b/src/IO.Ably.Tests.Shared/Rest/RestSpecs.cs index 2eb0a54bf..c37f3700c 100644 --- a/src/IO.Ably.Tests.Shared/Rest/RestSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Rest/RestSpecs.cs @@ -8,6 +8,7 @@ using IO.Ably.AcceptanceTests; using FluentAssertions; +using IO.Ably.Tests.Shared.Helpers; using Xunit; using Xunit.Abstractions; @@ -354,9 +355,10 @@ public void LogEvent(LogLevel level, string message) { } [Trait("spec", "TO3c")] public void WithLogHandler_ShouldUseNewLogHandler() { - _ = new AblyRest(new ClientOptions(ValidKey) { LogHandler = new TestLogHandler() }); + var restClient = new AblyRest(new ClientOptions(ValidKey) { LogHandler = new TestLogHandler() }); - Logger.LoggerSink.Should().BeOfType(); + restClient.Logger.LoggerSink.Should().NotBeOfType(); + restClient.Logger.LoggerSink.Should().BeOfType(); } [Fact] diff --git a/src/IO.Ably.Tests.Shared/Rest/SandboxSpecExtension.cs b/src/IO.Ably.Tests.Shared/Rest/SandboxSpecExtension.cs index 0e2cd1053..df9e6deab 100644 --- a/src/IO.Ably.Tests.Shared/Rest/SandboxSpecExtension.cs +++ b/src/IO.Ably.Tests.Shared/Rest/SandboxSpecExtension.cs @@ -30,7 +30,7 @@ internal static async Task WaitForState(this IRealtimeChannel channel, ChannelSt return; } - var channelAwaiter = new ChannelAwaiter(channel, awaitedState); + var channelAwaiter = new ChannelAwaiter(channel, awaitedState, ((RealtimeChannel)channel)?.Logger); var timespan = waitSpan.GetValueOrDefault(TimeSpan.FromSeconds(5)); Result result = await channelAwaiter.WaitAsync(timespan); if (result.IsFailure) diff --git a/src/IO.Ably.Tests.Shared/Rest/SandboxSpecs.cs b/src/IO.Ably.Tests.Shared/Rest/SandboxSpecs.cs index fb0f419d4..47a7e717a 100644 --- a/src/IO.Ably.Tests.Shared/Rest/SandboxSpecs.cs +++ b/src/IO.Ably.Tests.Shared/Rest/SandboxSpecs.cs @@ -6,6 +6,7 @@ using IO.Ably.Push; using IO.Ably.Realtime; using IO.Ably.Tests.Infrastructure; +using IO.Ably.Tests.Shared.Helpers; using Xunit; using Xunit.Abstractions; diff --git a/src/IO.Ably.Tests.Shared/Rest/TM3Spec.cs b/src/IO.Ably.Tests.Shared/Rest/TM3Spec.cs index 2e4413f59..6b617eb79 100644 --- a/src/IO.Ably.Tests.Shared/Rest/TM3Spec.cs +++ b/src/IO.Ably.Tests.Shared/Rest/TM3Spec.cs @@ -2,6 +2,7 @@ using System.Text; using FluentAssertions; using IO.Ably.Encryption; +using IO.Ably.Tests.Shared.Extensions; using Xunit; namespace IO.Ably.Tests.Rest @@ -14,7 +15,7 @@ public class TM3Spec public void Message_FromEncoded_WithNoEncoding() { var msg = new Message("name", "some-data"); - var fromEncoded = Message.FromEncoded(msg); + var fromEncoded = MessageExtensions.FromEncoded(msg); fromEncoded.Name.Should().Be("name"); fromEncoded.Data.Should().Be("some-data"); @@ -32,7 +33,7 @@ public void Message_FromEncoded_WithEncoding() }; var msg = new Message("name", JsonHelper.Serialize(d)) { Encoding = "json" }; - var fromEncoded = Message.FromEncoded(msg); + var fromEncoded = MessageExtensions.FromEncoded(msg); fromEncoded.Name.Should().Be("name"); JsonHelper.Serialize(fromEncoded.Data).Should().Be(JsonHelper.Serialize(d)); @@ -50,7 +51,7 @@ public void Message_FromEncoded_WithCustomEncoding() }; var msg = new Message("name", JsonHelper.Serialize(d)) { Encoding = "foo/json" }; - var fromEncoded = Message.FromEncoded(msg); + var fromEncoded = MessageExtensions.FromEncoded(msg); fromEncoded.Name.Should().Be("name"); JsonHelper.Serialize(fromEncoded.Data).Should().Be(JsonHelper.Serialize(d)); @@ -67,7 +68,7 @@ public void Message_FromEncoded_WithCipherEncoding() var payload = "payload".AddRandomSuffix(); var msg = new Message("name", crypto.Encrypt(Encoding.UTF8.GetBytes(payload))) { Encoding = "utf-8/cipher+aes-128-cbc" }; - var fromEncoded = Message.FromEncoded(msg, new ChannelOptions(cipherParams)); + var fromEncoded = MessageExtensions.FromEncoded(msg, new ChannelOptions(cipherParams)); fromEncoded.Name.Should().Be("name"); fromEncoded.Data.Should().BeEquivalentTo(payload); @@ -83,7 +84,7 @@ public void Message_FromEncoded_WithInvalidCipherEncoding() var msg = new Message("name", Encoding.UTF8.GetBytes(payload)) { Encoding = "utf-8/cipher+aes-128-cbc" }; - Assert.Throws(() => Message.FromEncoded(msg, new ChannelOptions(cipherParams))); + Assert.Throws(() => MessageExtensions.FromEncoded(msg, new ChannelOptions(cipherParams))); } [Fact] @@ -96,7 +97,7 @@ public void Message_FromEncodedArray_WithNoEncoding() new Message("name2", "some-data2") }; - var fromEncoded = Message.FromEncodedArray(msg); + var fromEncoded = MessageExtensions.FromEncodedArray(msg); fromEncoded.Should().HaveCount(2); @@ -123,7 +124,7 @@ public void Message_FromEncoded_WithJsonString() var msg = new Message("name", JsonHelper.Serialize(d)) { Encoding = "foo/json" }; var msgJson = JsonHelper.Serialize(msg); - var fromEncoded = Message.FromEncoded(msgJson); + var fromEncoded = MessageExtensions.FromEncoded(msgJson); fromEncoded.Name.Should().Be("name"); JsonHelper.Serialize(fromEncoded.Data).Should().Be(JsonHelper.Serialize(d)); @@ -142,7 +143,7 @@ public void Message_FromEncoded_WithCipherEncoding() var encryptedString = crypto.Encrypt(payload.GetBytes()); var msg = new Message("name", encryptedString.ToBase64()) { Encoding = "utf-8/cipher+aes-128-cbc/base64" }; var msgJson = JsonHelper.Serialize(msg); - var fromEncoded = Message.FromEncoded(msgJson, new ChannelOptions(cipherParams)); + var fromEncoded = MessageExtensions.FromEncoded(msgJson, new ChannelOptions(cipherParams)); fromEncoded.Name.Should().Be("name"); fromEncoded.Data.Should().BeEquivalentTo(payload); @@ -161,7 +162,7 @@ public void Message_FromEncodedArray_WithJsonArray() var messagesJson = JsonHelper.Serialize(messages); - var fromEncoded = Message.FromEncodedArray(messagesJson); + var fromEncoded = MessageExtensions.FromEncodedArray(messagesJson); fromEncoded.Should().HaveCount(2); diff --git a/src/IO.Ably.Tests.Shared/Samples/DocumentationSamples.cs b/src/IO.Ably.Tests.Shared/Samples/DocumentationSamples.cs index 7cd82011f..be8709d7d 100644 --- a/src/IO.Ably.Tests.Shared/Samples/DocumentationSamples.cs +++ b/src/IO.Ably.Tests.Shared/Samples/DocumentationSamples.cs @@ -5,6 +5,7 @@ using IO.Ably.Encryption; using IO.Ably.Realtime; +using IO.Ably.Tests.Shared.Helpers; namespace IO.Ably.Tests.Samples { diff --git a/src/IO.Ably.iOS/Platform.cs b/src/IO.Ably.iOS/Platform.cs index 6c65efa27..d29a2281d 100644 --- a/src/IO.Ably.iOS/Platform.cs +++ b/src/IO.Ably.iOS/Platform.cs @@ -1,27 +1,24 @@ -using IO.Ably.Transport; -using System.Net.NetworkInformation; -using IO.Ably.Push; +using System.Net.NetworkInformation; using IO.Ably.Realtime; namespace IO.Ably { internal class Platform : IPlatform { - private static readonly object _lock = new object(); - - internal static bool HookedUpToNetworkEvents { get; private set; } public Agent.PlatformRuntime PlatformId => Agent.PlatformRuntime.XamarinIos; - public ITransportFactory TransportFactory => null; - public IMobileDevice MobileDevice { get; set; } - public void RegisterOsNetworkStateChanged() + private static readonly object Lock = new object(); + + internal static bool HookedUpToNetworkEvents { get; set; } + + public void RegisterOsNetworkStateChanged(ILogger logger) { - lock (_lock) + lock (Lock) { if (HookedUpToNetworkEvents == false) { NetworkChange.NetworkAvailabilityChanged += (sender, eventArgs) => - Connection.NotifyOperatingSystemNetworkState(eventArgs.IsAvailable ? NetworkState.Online : NetworkState.Offline); + Connection.NotifyOperatingSystemNetworkState(eventArgs.IsAvailable ? NetworkState.Online : NetworkState.Offline, logger); } HookedUpToNetworkEvents = true;