Program.cs (7160B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 4 namespace LocalhostWebServer 5 { 6 using Microsoft.AspNetCore.Builder; 7 using Microsoft.AspNetCore.Hosting; 8 using Microsoft.Extensions.Hosting; 9 using System; 10 using System.IO; 11 using Microsoft.Extensions.Configuration; 12 using System.Security.Cryptography.X509Certificates; 13 using System.Text.Json.Serialization; 14 using System.Text.Json; 15 using WinGetSourceCreator.Model; 16 using Microsoft.WinGetSourceCreator; 17 using System.Runtime.InteropServices; 18 19 public class Program 20 { 21 const string CertificateProviderString = "Microsoft.PowerShell.Security\\Certificate::"; 22 const string StoreLocationCurrentUser = "CurrentUser"; 23 const string StoreLocationLocalMachine = "LocalMachine"; 24 25 static void Main(string[] args) 26 { 27 IConfiguration config = new ConfigurationBuilder() 28 .AddCommandLine(args) 29 .Build(); 30 31 Startup.StaticFileRoot = config.GetValue<string>("StaticFileRoot"); 32 Startup.CertPath = config.GetValue<string>("CertPath"); 33 Startup.CertPassword = config.GetValue<string>("CertPassword"); 34 Startup.Port = config.GetValue<Int32>("Port", 5001); 35 Startup.OutCertFile = config.GetValue<string>("OutCertFile"); 36 Startup.LocalSourceJson = config.GetValue<string>("LocalSourceJson"); 37 Startup.TestDataPath = config.GetValue<string>("TestDataPath"); 38 Startup.ExitBeforeRun = config.GetValue<bool>("ExitBeforeRun"); 39 40 if (string.IsNullOrEmpty(Startup.StaticFileRoot) || 41 string.IsNullOrEmpty(Startup.CertPath)) 42 { 43 Console.WriteLine("Usage: LocalhostWebServer.exe StaticFileRoot=<Path to Serve Static Root Directory> " + 44 "CertPath=<Path to HTTPS Developer Certificate> CertPassword=<Certificate Password> <Port=Port Number> <PutCertInRoot=Boolean>"); 45 return; 46 } 47 48 Directory.CreateDirectory(Startup.StaticFileRoot); 49 50 if (Startup.CertPath.StartsWith(CertificateProviderString)) 51 { 52 string certPath = Startup.CertPath.Substring(CertificateProviderString.Length); 53 string[] pathParts = certPath.Split('\\'); 54 55 if (pathParts.Length != 3) 56 { 57 throw new InvalidDataException($"Don't know how to handle: {Startup.CertPath}"); 58 } 59 60 StoreLocation storeLocation = StoreLocation.CurrentUser; 61 if (pathParts[0] == StoreLocationCurrentUser) 62 { 63 // The default 64 } 65 else if (pathParts[0] == StoreLocationLocalMachine) 66 { 67 storeLocation = StoreLocation.LocalMachine; 68 } 69 else 70 { 71 throw new InvalidDataException($"Unknown store scope: {Startup.CertPath}"); 72 } 73 74 X509Store x509Store = new X509Store(pathParts[1], storeLocation); 75 x509Store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); 76 X509Certificate2Collection collection = x509Store.Certificates; 77 78 if (collection.Count == 0) 79 { 80 throw new InvalidDataException($"Found {collection.Count} certificates in store '{pathParts[0]}' [{storeLocation}] \\ '{pathParts[1]}': {Startup.CertPath}"); 81 } 82 83 X509Certificate2Collection results = collection.Find(X509FindType.FindByThumbprint, pathParts[2], true); 84 85 if (results.Count != 1) 86 { 87 throw new InvalidDataException($"Found {results.Count} matches for '{pathParts[2]}': {Startup.CertPath}"); 88 } 89 90 ServerCertificate = results[0]; 91 } 92 else 93 { 94 ServerCertificate = new X509Certificate2(Startup.CertPath, Startup.CertPassword); 95 } 96 97 if (!string.IsNullOrEmpty(Startup.OutCertFile)) 98 { 99 string parent = Path.GetDirectoryName(Startup.OutCertFile); 100 if (!string.IsNullOrEmpty(parent)) 101 { 102 Directory.CreateDirectory(parent); 103 } 104 105 File.WriteAllBytes(Startup.OutCertFile, ServerCertificate.Export(X509ContentType.Cert)); 106 } 107 108 if (!string.IsNullOrEmpty(Startup.LocalSourceJson)) 109 { 110 if (!File.Exists(Startup.LocalSourceJson)) 111 { 112 throw new FileNotFoundException(Startup.LocalSourceJson); 113 } 114 115 WinGetLocalSource.CreateFromLocalSourceFile(Startup.LocalSourceJson); 116 } 117 118 if (!string.IsNullOrEmpty(Startup.TestDataPath)) 119 { 120 if (!Directory.Exists(Startup.TestDataPath)) 121 { 122 throw new DirectoryNotFoundException(Startup.TestDataPath); 123 } 124 125 var testDataDirectory = Path.Combine(Startup.StaticFileRoot, "TestData"); 126 Directory.CreateDirectory(testDataDirectory); 127 128 CopyDirectoryRecursive(Startup.TestDataPath, testDataDirectory); 129 } 130 131 if (Startup.ExitBeforeRun) 132 { 133 return; 134 } 135 136 CreateHostBuilder(args).Build().Run(); 137 } 138 139 public static IHostBuilder CreateHostBuilder(string[] args) => 140 Host.CreateDefaultBuilder(args) 141 .ConfigureWebHostDefaults(webBuilder => 142 { 143 webBuilder.UseKestrel(opt => 144 { 145 opt.ListenAnyIP(Startup.Port, listOpt => 146 { 147 listOpt.UseHttps(ServerCertificate); 148 }); 149 }); 150 webBuilder.UseContentRoot(Startup.StaticFileRoot); 151 webBuilder.UseStartup<Startup>(); 152 }); 153 154 public static X509Certificate2 ServerCertificate { get; private set; } 155 156 private static void CopyDirectoryRecursive(string sourceDir, string destDir) 157 { 158 if (!Directory.Exists(destDir)) 159 { 160 Directory.CreateDirectory(destDir); 161 } 162 163 string[] files = Directory.GetFiles(sourceDir); 164 foreach (string file in files) 165 { 166 string dest = Path.Combine(destDir, Path.GetFileName(file)); 167 File.Copy(file, dest, overwrite: true); 168 } 169 170 string[] directories = Directory.GetDirectories(sourceDir); 171 foreach (string dir in directories) 172 { 173 string dest = Path.Combine(destDir, Path.GetFileName(dir)); 174 CopyDirectoryRecursive(dir, dest); 175 } 176 } 177 } 178 }