wasStitchNET – Rev 13

Subversion Repositories:
Rev:
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2017 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using MaxMind.GeoIP2.Responses;
using wasDAVClient;
using wasSharp.Geo;
using wasStitchNET.GeoIP;
using wasStitchNET.Structures;

namespace wasStitchNET.Repository
{
    public static class Mirrors
    {
        /// <summary>
        ///     Returns a list of valid Stitch mirrors.
        /// </summary>
        /// <param name="localCity">the local city from where the method is invoked</param>
        /// <param name="mirror">the mirror to get the distance to</param>
        /// <param name="client">the was DAV client to use</param>
        /// <param name="server">the Stitch server to query</param>
        /// <returns>a list of discovered Stitch mirrors</returns>
        public static IEnumerable<StitchMirror> InitializeMirrors(CityResponse localCity, Client client, string server)
        {
            Func<string, StitchMirror> computeMirrorDistance = o =>
            {
                var mirrorDistance = GetMirrorDistance(localCity, o);
                if (mirrorDistance.Equals(default(KeyValuePair<string, Distance>)))
                    return default(StitchMirror);
                return new StitchMirror(mirrorDistance.Key, mirrorDistance.Value);
            };

            var mirrors = new List<string> {server};
            yield return computeMirrorDistance(server);

            foreach (var stitchMirror in mirrors.AsParallel().SelectMany(
                mirror => FetchStitchMirrors(client, mirror)
                    .AsParallel()
                    .Where(o => !string.IsNullOrEmpty(o) && !mirrors.Contains(o))
                    .Select(computeMirrorDistance)))
            {
                if (stitchMirror.Equals(default(StitchMirror)))
                    continue;
                mirrors.Add(stitchMirror.Address);
                yield return stitchMirror;
            }
        }

        /// <summary>
        ///     Retrieves a list of mirrors from a Stitch repository.
        /// </summary>
        /// <param name="client">the was DAV client to use</param>
        /// <param name="server">the Stitch server to retrieve the mirrors from</param>
        /// <returns>a list of mirrors</returns>
        public static IEnumerable<string> FetchStitchMirrors(Client client, string server)
        {
            // Set the server.
            client.Server = server;

            // Set the mirror path
            var mirrorsPath = @"/" +
                              string.Join(@"/", STITCH_CONSTANTS.UPDATE_PATH, STITCH_CONSTANTS.PROGRESSIVE_PATH,
                                  STITCH_CONSTANTS.UPDATE_MIRRORS_FILE);

            using (var stream = client.Download(mirrorsPath).Result)
            {
                if (stream == null)
                    yield break;

                using (var streamReader = new StreamReader(stream))
                {
                    string mirror;
                    do
                    {
                        mirror = streamReader.ReadLine();

                        if (string.IsNullOrEmpty(mirror))
                            continue;

                        yield return mirror;
                    } while (!string.IsNullOrEmpty(mirror));
                }
            }
        }

        /// <summary>
        ///     Gets the distance to a mirror.
        /// </summary>
        /// <param name="localCity">the local city from where the method is invoked</param>
        /// <param name="mirror">the mirror to get the distance to</param>
        /// <returns>a key-value pair of mirror by distance</returns>
        public static KeyValuePair<string, Distance> GetMirrorDistance(CityResponse localCity, string mirror)
        {
            // Check that the mirror has a proper URI.
            Uri mirrorUri;
            if (!Uri.TryCreate(mirror, UriKind.Absolute, out mirrorUri))
                return default(KeyValuePair<string, Distance>);

            // If we do not know the local city, then just return the mirror.
            if (localCity == null)
                return new KeyValuePair<string, Distance>(mirror, null);

            // Resolve the mirror hostname to an IP address.
            IPAddress address;
            try
            {
                address = Dns.GetHostAddresses(mirrorUri.Host).FirstOrDefault();
            }
            catch (Exception)
            {
                return
                    new KeyValuePair<string, Distance>(mirror, null);
            }

            // Resolve the IP address to a city response.
            var remoteCity = address.GeoIPGetCity();
            if (remoteCity == null)
                return new KeyValuePair<string, Distance>(mirror, null);

            // Compute the distance to the mirror.
            switch (remoteCity.Location.HasCoordinates)
            {
                case true:
                    var local = new GeographicCoordinate(localCity.Location.Latitude.Value,
                        localCity.Location.Longitude.Value);
                    var remote = new GeographicCoordinate(remoteCity.Location.Latitude.Value,
                        remoteCity.Location.Longitude.Value);
                    return
                        new KeyValuePair<string, Distance>(mirror, local.HaversineDistanceTo(remote));
                default:
                    return
                        new KeyValuePair<string, Distance>(mirror, null);
            }
        }
    }
}

Generated by GNU Enscript 1.6.5.90.