/// <summary>
        /// Test latency (ping) to server
        /// </summary>
        /// <returns>Latency in milliseconds (ms)</returns>
        public int TestServerLatency(Server server, int retryCount = 3)
        {
            var latencyUri = CreateTestUrl(server, "latency.txt");
            var timer      = new Stopwatch();

            using (var client = new SpeedTestHttpClient())
            {
                for (var i = 0; i < retryCount; i++)
                {
                    string testString;
                    try
                    {
                        timer.Start();
                        testString = client.GetStringAsync(latencyUri).ConfigureAwait(false).GetAwaiter().GetResult();
                    }
                    catch (WebException)
                    {
                        continue;
                    }
                    finally
                    {
                        timer.Stop();
                    }

                    if (!testString.StartsWith("test=test"))
                    {
                        throw new InvalidOperationException("Server returned incorrect test string for latency.txt");
                    }
                }
            }

            return((int)timer.ElapsedMilliseconds / retryCount);
        }
        private static double TestSpeed <T>(IEnumerable <T> testData, Func <HttpClient, T, Task <int> > doWork, int concurencyCount = 2)
        {
            var timer     = new Stopwatch();
            var throttler = new SemaphoreSlim(concurencyCount);

            timer.Start();
            var downloadTasks = testData.Select(async data =>
            {
                await throttler.WaitAsync().ConfigureAwait(false);
                var client = new SpeedTestHttpClient();
                try
                {
                    var size = await doWork(client, data).ConfigureAwait(false);
                    return(size);
                }
                finally
                {
                    client.Dispose();
                    throttler.Release();
                }
            }).ToArray();

            Task.WaitAll(downloadTasks);
            timer.Stop();

            double totalSize = downloadTasks.Sum(task => task.Result);

            return((totalSize * 8 / 1024) / ((double)timer.ElapsedMilliseconds / 1000));
        }
        private const int MaxUploadSize      = 4; // 400 KB

        #region ISpeedTestClient

        /// <summary>
        /// Download speedtest.net settings
        /// </summary>
        /// <returns>speedtest.net settings</returns>
        public Settings GetSettings()
        {
            using (var client = new SpeedTestHttpClient())
            {
                var settings      = client.GetConfig <Settings>(ConfigUrl).GetAwaiter().GetResult();
                var serversConfig = client.GetConfig <ServersList>(ServersUrl).GetAwaiter().GetResult();

                serversConfig.CalculateDistances(settings.Client.GeoCoordinate);
                settings.Servers = serversConfig.Servers.OrderBy(s => s.Distance).ToList();

                return(settings);
            }
        }