using System; using System.IO; using System.Net; using System.Text; using JetBrains.Annotations; using NugetForUnity.Configuration; using UnityEngine; namespace NugetForUnity.Helper { /// /// Helper class for making web requests using . /// internal static class WebRequestHelper { /// /// Get the specified URL from the web. Throws exceptions if the request fails. /// /// URL that will be loaded. /// UserName that will be passed in the Authorization header or the request. If null, authorization is omitted. /// Password that will be passed in the Authorization header or the request. If null, authorization is omitted. /// Timeout in milliseconds or null to use the default timeout values of HttpWebRequest. /// Stream containing the result. [NotNull] internal static Stream RequestUrl([NotNull] string url, [CanBeNull] string userName, [CanBeNull] string password, int? timeOut) { // Mono doesn't have a Certificate Authority, so we have to provide all validation manually. Currently just accept anything. // See here: http://stackoverflow.com/questions/4926676/mono-webrequest-fails-with-https ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true; try { #pragma warning disable SYSLIB0014 // Type or member is obsolete var getRequest = (HttpWebRequest)WebRequest.Create(url); #pragma warning restore SYSLIB0014 // Type or member is obsolete getRequest.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.None; getRequest.Timeout = timeOut ?? ConfigurationManager.NugetConfigFile.RequestTimeoutSeconds * 1000; if (string.IsNullOrEmpty(password)) { var credentials = CredentialProviderHelper.GetCredentialFromProvider(getRequest.RequestUri); if (credentials.HasValue) { userName = credentials.Value.UserName; password = credentials.Value.Password; } } if (password != null) { // Send password as described by https://docs.microsoft.com/en-us/vsts/integrate/get-started/rest/basics. // This works with Visual Studio Team Services, but hasn't been tested with other authentication schemes so there may be additional work needed if there // are different kinds of authentication. getRequest.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes($"{userName}:{password}"))); } NugetLogger.LogVerbose("HTTP GET {0}", url); var objStream = getRequest.GetResponse().GetResponseStream(); return objStream ?? throw new InvalidOperationException("Response stream is null."); } catch (WebException webException) { WarnIfDotNetAuthenticationIssue(webException); throw; } } /// /// Checks if the exception is due to a known issue with .NET 3.5 and authentication and if so a custom warning is created.. /// /// The exception to check. private static void WarnIfDotNetAuthenticationIssue([NotNull] WebException webException) { if (webException.Response is HttpWebResponse webResponse && webResponse.StatusCode == HttpStatusCode.BadRequest && webException.Message.IndexOf( "Authentication information is not given in the correct format", StringComparison.OrdinalIgnoreCase) >= 0) { // This error occurs when downloading a package with authentication using .NET 3.5, but seems to be fixed by the new .NET 4.6 runtime. // Inform users when this occurs. Debug.LogError( "Authentication failed. This can occur due to a known issue in .NET 3.5. This can be fixed by changing Scripting Runtime to Experimental (.NET 4.6 Equivalent) in Player Settings."); } } } }