/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); |
} |
} |
} |
} |