diff --git a/StoreLib/Services/DisplayCatalogHandler.cs b/StoreLib/Services/DisplayCatalogHandler.cs index e3a079c..04c5045 100644 --- a/StoreLib/Services/DisplayCatalogHandler.cs +++ b/StoreLib/Services/DisplayCatalogHandler.cs @@ -10,6 +10,8 @@ namespace StoreLib.Services { public class DisplayCatalogHandler { + private readonly MSHttpClient _httpClient; + public DisplayCatalogModel ProductListing { get; internal set; } public Exception Error { get; internal set; } internal Uri ConstructedUri { get; set; } @@ -24,6 +26,9 @@ public class DisplayCatalogHandler public DisplayCatalogHandler(DCatEndpoint SelectedEndpoint, Locale Locale) { + //Adds needed headers for MS related requests. See MS_httpClient.cs + this._httpClient = new MSHttpClient(); + this.SelectedEndpoint = SelectedEndpoint; this.SelectedLocale = Locale; } @@ -59,14 +64,13 @@ public async Task QueryDCATAsync(string ID) this.ID = ID; this.ConstructedUri = Utilities.UriHelpers.CreateAlternateDCatUri(SelectedEndpoint, ID, IdentiferType.ProductID, SelectedLocale); Result = new DisplayCatalogResult(); //We need to clear the result incase someone queries a product, then queries a not found one, the wrong product will be returned. - MSHttpClient httpClient = new MSHttpClient(); //Adds needed headers for MS related requests. See MSHttpClient.cs HttpResponseMessage httpResponse = new HttpResponseMessage(); HttpRequestMessage httpRequestMessage; //We need to build the request URL based on the requested EndPoint;httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, ConstructedUri); httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, ConstructedUri); try { - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); } catch (TaskCanceledException) { @@ -101,14 +105,13 @@ public async Task QueryDCATAsync(string ID, IdentiferType IDType) this.ID = ID; this.ConstructedUri = Utilities.UriHelpers.CreateAlternateDCatUri(SelectedEndpoint, ID, IDType, SelectedLocale); Result = new DisplayCatalogResult(); //We need to clear the result incase someone queries a product, then queries a not found one, the wrong product will be returned. - MSHttpClient httpClient = new MSHttpClient(); //Adds needed headers for MS related requests. See MSHttpClient.cs HttpResponseMessage httpResponse = new HttpResponseMessage(); HttpRequestMessage httpRequestMessage; //We need to build the request URL based on the requested EndPoint;httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, ConstructedUri); httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, ConstructedUri); try { - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); } catch (TaskCanceledException) { @@ -143,15 +146,14 @@ public async Task QueryDCATAsync(string ID, IdentiferType IDType) this.ID = ID; this.ConstructedUri = Utilities.UriHelpers.CreateAlternateDCatUri(SelectedEndpoint, ID, IdentiferType.ProductID, SelectedLocale); Result = new DisplayCatalogResult(); //We need to clear the result incase someone queries a product, then queries a not found one, the wrong product will be returned. - MSHttpClient httpClient = new MSHttpClient(); //MSHttpClient extends System.Net.HttpClient to automaticly add a (needed) CorrelationVector to each request. HttpResponseMessage httpResponse = new HttpResponseMessage(); HttpRequestMessage httpRequestMessage; - httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authentication", AuthenticationToken); //We need to build the request URL based on the requested EndPoint; httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, ConstructedUri); + httpRequestMessage.Headers.TryAddWithoutValidation("Authentication", AuthenticationToken); try { - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); } catch (TaskCanceledException) { @@ -186,15 +188,14 @@ public async Task QueryDCATAsync(string ID, IdentiferType IDType) this.ID = ID; this.ConstructedUri = Utilities.UriHelpers.CreateAlternateDCatUri(SelectedEndpoint, ID, IDType, SelectedLocale); Result = new DisplayCatalogResult(); //We need to clear the result incase someone queries a product, then queries a not found one, the wrong product will be returned. - MSHttpClient httpClient = new MSHttpClient(); //MSHttpClient extends System.Net.HttpClient to automaticly add a (needed) CorrelationVector to each request. HttpResponseMessage httpResponse = new HttpResponseMessage(); HttpRequestMessage httpRequestMessage; - httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authentication", AuthenticationToken); //We need to build the request URL based on the requested EndPoint; httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, ConstructedUri); + httpRequestMessage.Headers.TryAddWithoutValidation("Authentication", AuthenticationToken); try { - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); } catch (TaskCanceledException) { @@ -224,46 +225,45 @@ public async Task QueryDCATAsync(string ID, IdentiferType IDType) /// Instance of DCatSearch, containing the returned products. public async Task SearchDCATAsync(string Query, DeviceFamily deviceFamily) { - MSHttpClient httpClient = new MSHttpClient(); HttpResponseMessage httpResponse = new HttpResponseMessage(); HttpRequestMessage httpRequestMessage; switch (deviceFamily) { case DeviceFamily.Desktop: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Desktop"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.Xbox: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Xbox"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.Universal: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Universal"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.Mobile: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Mobile"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.HoloLens: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Holographic"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.IotCore: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Iot"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.ServerCore: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Server"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.Andromeda: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.8828080"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.WCOS: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Core"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; } if (httpResponse.IsSuccessStatusCode) @@ -304,42 +304,41 @@ public async Task> GetAddonsForProductAsync() /// public async Task SearchDCATAsync(string Query, DeviceFamily DeviceFamily, int SkipCount) { - MSHttpClient httpClient = new MSHttpClient(); HttpResponseMessage httpResponse = new HttpResponseMessage(); HttpRequestMessage httpRequestMessage; switch (DeviceFamily) { case DeviceFamily.Desktop: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Desktop"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.Xbox: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Xbox"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.Universal: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Universal"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.Mobile: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Mobile"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.HoloLens: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Holographic"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.IotCore: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Iot"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.ServerCore: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.Server"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; case DeviceFamily.Andromeda: httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, $"{Utilities.TypeHelpers.EnumToSearchUri(SelectedEndpoint)}{Query}&productFamilyNames=apps,games&platformDependencyName=Windows.8828080"); - httpResponse = await httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); + httpResponse = await _httpClient.SendAsync(httpRequestMessage, new System.Threading.CancellationToken()); break; } if (httpResponse.IsSuccessStatusCode) diff --git a/StoreLib/Services/FE3Handler.cs b/StoreLib/Services/FE3Handler.cs index f342339..89502aa 100644 --- a/StoreLib/Services/FE3Handler.cs +++ b/StoreLib/Services/FE3Handler.cs @@ -15,6 +15,8 @@ namespace StoreLib.Services { public static class FE3Handler { + private static readonly MSHttpClient _httpClient = new MSHttpClient(); + /// /// Returns raw xml containing various (Revision, Update, Package) IDs and info. /// @@ -22,13 +24,12 @@ public static class FE3Handler /// public static async Task SyncUpdatesAsync(string WuCategoryID) { - MSHttpClient httpClient = new MSHttpClient(); HttpContent httpContent = new StringContent(String.Format(GetResourceTextFile("WUIDRequest.xml"), await GetCookieAsync(), WuCategoryID), Encoding.UTF8, "application/soap+xml"); //Load in the Xml for this FE3 request and format it a cookie and the provided WuCategoryID. HttpRequestMessage httpRequest = new HttpRequestMessage(); httpRequest.RequestUri = Endpoints.FE3Delivery; httpRequest.Content = httpContent; httpRequest.Method = HttpMethod.Post; - HttpResponseMessage httpResponse = await httpClient.SendAsync(httpRequest, new System.Threading.CancellationToken()); + HttpResponseMessage httpResponse = await _httpClient.SendAsync(httpRequest, new System.Threading.CancellationToken()); string content = await httpResponse.Content.ReadAsStringAsync(); content = HttpUtility.HtmlDecode(content); return content; @@ -40,14 +41,13 @@ public static async Task SyncUpdatesAsync(string WuCategoryID) /// Cookie extracted from returned XML public static async Task GetCookieAsync() //Encrypted Cookie Data is needed for FE3 requests. It doesn't expire for a very long time but I still refresh it as the Store does. { - MSHttpClient httpClient = new MSHttpClient(); XmlDocument doc = new XmlDocument(); HttpContent httpContent = new StringContent(GetResourceTextFile("GetCookie.xml"), Encoding.UTF8, "application/soap+xml");//Loading the request xml from a file to keep things nice and tidy. HttpRequestMessage httpRequest = new HttpRequestMessage(); httpRequest.RequestUri = Endpoints.FE3Delivery; httpRequest.Content = httpContent; httpRequest.Method = HttpMethod.Post; - HttpResponseMessage httpResponse = await httpClient.SendAsync(httpRequest, new System.Threading.CancellationToken()); + HttpResponseMessage httpResponse = await _httpClient.SendAsync(httpRequest, new System.Threading.CancellationToken()); doc.LoadXml(await httpResponse.Content.ReadAsStringAsync()); XmlNodeList xmlNodeList = doc.GetElementsByTagName("EncryptedData"); string cookie = xmlNodeList[0].InnerText; @@ -84,7 +84,6 @@ public static void ProcessUpdateIDs(string Xml, out IList RevisionIDs, o /// IList of App Package Download Uris public static async Task> GetFileUrlsAsync(IList UpdateIDs, IList RevisionIDs) { - MSHttpClient httpClient = new MSHttpClient(); XmlDocument doc = new XmlDocument(); IList uris = new List(); foreach (string ID in UpdateIDs) @@ -94,7 +93,7 @@ public static async Task> GetFileUrlsAsync(IList UpdateIDs, I httpRequest.RequestUri = Endpoints.FE3DeliverySecured; httpRequest.Content = httpContent; httpRequest.Method = HttpMethod.Post; - HttpResponseMessage httpResponse = await httpClient.SendAsync(httpRequest, new System.Threading.CancellationToken()); + HttpResponseMessage httpResponse = await _httpClient.SendAsync(httpRequest, new System.Threading.CancellationToken()); doc.LoadXml(await httpResponse.Content.ReadAsStringAsync()); XmlNodeList XmlUrls = doc.GetElementsByTagName("FileLocation"); foreach (XmlNode node in XmlUrls) diff --git a/StoreLib/Services/MSHttpClient.cs b/StoreLib/Services/MSHttpClient.cs index a8de687..db58538 100644 --- a/StoreLib/Services/MSHttpClient.cs +++ b/StoreLib/Services/MSHttpClient.cs @@ -10,23 +10,58 @@ namespace StoreLib.Services { public class MSHttpClient : HttpClient - {/// - /// An override of the SendAsync Function from HttpClient. This is done to automatically add the needed MS-CV tracking header to every request (along with our user-agent). - /// - /// - /// - /// - public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) //Overriding the SendAsync so we can easily add the CorrelationVector and User-Agent to every request. + { + private static readonly bool IsWindows = System.Runtime.InteropServices.RuntimeInformation + .IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows); + private static HttpClientHandler _handler { - using (HttpClient client = new HttpClient()) + get { - CorrelationVector CV = new CorrelationVector(); - CV.Init(); - request.Headers.Add("MS-CV", CV.GetValue()); - request.Headers.TryAddWithoutValidation("User-Agent", "StoreLib"); - HttpResponseMessage response = await client.SendAsync(request); - return response; + HttpClientHandler handler = new HttpClientHandler(); + if (!IsWindows) + { + handler.ServerCertificateCustomValidationCallback = ServerCertificateValidationCallback; + } + return handler; } } + + private static bool ServerCertificateValidationCallback( + object sender, + System.Security.Cryptography.X509Certificates.X509Certificate certificate, + System.Security.Cryptography.X509Certificates.X509Chain chain, + System.Net.Security.SslPolicyErrors sslPolicyErrors + ) + { + + // TODO: Refine + return true; + } + + private readonly CorrelationVector _cv = new CorrelationVector(); + + /// + /// Instantiate MSHttpClient + /// + public MSHttpClient() + : base(_handler) + { + _cv.Init(); + base.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "StoreLib"); + } + + /// + /// An override of the SendAsync Function from HttpClient. This is done to automatically add the needed MS-CV tracking header to every request (along with our user-agent). + /// + /// + /// + /// + public override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) //Overriding the SendAsync so we can easily add the CorrelationVector and User-Agent to every request. + { + request.Headers.Add("MS-CV", _cv.GetValue()); + _cv.Increment(); + HttpResponseMessage response = await base.SendAsync(request); + return response; + } } } diff --git a/StoreLib/Utilities/CorrelationVector.cs b/StoreLib/Utilities/CorrelationVector.cs index f0a8651..15cebfc 100644 --- a/StoreLib/Utilities/CorrelationVector.cs +++ b/StoreLib/Utilities/CorrelationVector.cs @@ -65,8 +65,7 @@ internal void Init() protected static int getCllSettingsAsInt(Settings setting) { - int asInt = Int32.Parse(setting.ToString()); - return asInt; + return (int)setting; } private bool CanExtend()