wasStitchNET

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 12  →  ?path2? @ 13
/Repository/Mirrors.cs
@@ -0,0 +1,143 @@
///////////////////////////////////////////////////////////////////////////
// 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);
}
}
}
}