From eecf27e75742246164bcf518a68afdbf0cd5bb5b Mon Sep 17 00:00:00 2001 From: Kaveman <55042475+Kaveinator@users.noreply.github.com> Date: Sun, 18 Aug 2024 04:14:04 -0700 Subject: [PATCH] Remove Webserver Will be re-added as submodule --- WebServer/AreaBase.cs | 90 --- WebServer/Extensions/ExpandoObject.cs | 24 - WebServer/HttpConfiguration.cs | 35 -- WebServer/HttpResponse.cs | 33 - WebServer/HttpServer.cs | 371 ----------- WebServer/IHttpLogger.cs | 13 - WebServer/Models/StatusPageModel.cs | 38 -- WebServer/ResponseTimedOutException.cs | 26 - WebServer/Utils/CachedResponse.cs | 48 -- WebServer/Utils/MimeTypeMap.cs | 829 ------------------------- WebServer/WebServer.csproj | 13 - 11 files changed, 1520 deletions(-) delete mode 100644 WebServer/AreaBase.cs delete mode 100644 WebServer/Extensions/ExpandoObject.cs delete mode 100644 WebServer/HttpConfiguration.cs delete mode 100644 WebServer/HttpResponse.cs delete mode 100644 WebServer/HttpServer.cs delete mode 100644 WebServer/IHttpLogger.cs delete mode 100644 WebServer/Models/StatusPageModel.cs delete mode 100644 WebServer/ResponseTimedOutException.cs delete mode 100644 WebServer/Utils/CachedResponse.cs delete mode 100644 WebServer/Utils/MimeTypeMap.cs delete mode 100644 WebServer/WebServer.csproj diff --git a/WebServer/AreaBase.cs b/WebServer/AreaBase.cs deleted file mode 100644 index d422fad..0000000 --- a/WebServer/AreaBase.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Microsoft.Extensions.Hosting; -using RazorLight; -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Net; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using WebServer.Models; - -namespace WebServer { - public abstract class AreaBase { // Formally `HttpEndpointHandler` - // Provides razor pages, resources and relevent db tables and entities - public readonly HttpServer Server; - public readonly RazorLightEngine RazorEngine; - public readonly string Hostname; - public readonly string AreaPath; - public readonly AreaBase? ParentArea; - - public AreaBase(HttpServer server, string host) : this(server, host, string.Empty) { } - - public AreaBase(HttpServer server, string host, string areaPath) { - if (server == null) throw new ArgumentNullException(nameof(server)); - if (string.IsNullOrEmpty(host)) throw new ArgumentNullException(nameof(host)); - Server = server; - RazorEngine = server.GetOrCreateRazorEngine(host); - Hostname = host; - AreaPath = areaPath ?? string.Empty; - } - - public AreaBase(AreaBase parentArea, string relativeAreaPath) { // So you can build on top of other areas - ParentArea = parentArea; - Server = parentArea.Server; - RazorEngine = parentArea.RazorEngine; - Hostname = parentArea.Hostname; - AreaPath = $"{parentArea.AreaPath}/{relativeAreaPath.Trim(' ', '/', '\\')}"; - } - - public static Uri BuildUri(HttpListenerRequest request, string path) { - string uriString = $"{(request.IsSecureConnection ? "https" : "http")}://{request.UserHostName}/"; - if (string.IsNullOrEmpty(path)) - return new Uri(uriString); - path = path.Replace('\\', '/'); - if (path[0] == '/') - path = path.Substring(1); - uriString += path; - return new Uri(uriString); - } - - #region Razor Stuff - public async Task GetGenericStatusPageAsync(StatusPageModel model, ExpandoObject? viewBag = null) { - return await Server.GetGenericStatusPageAsync(model, Hostname, viewBag); - } - public async Task GetGenericStatusPageAsync(HttpStatusCode statusCode, ExpandoObject? viewBag = null) - => await GetGenericStatusPageAsync(new StatusPageModel(statusCode), viewBag); - - /// Renders a View with a T model - /// Type of the model - /// The view path relative to the AreaName, keep in mind that prepending a '/' (or '\') will go to root of the top-most area - /// The model supplied to the view - /// A string of the compiled view - public async Task RenderAsync(string viewPath, T model, ExpandoObject viewBag = null) { // Formally Try/GetTemplate - viewPath = viewPath.Trim().Replace('\\', '/'); - string root = viewPath[0] == '/' ? "" : AreaPath; - return await RazorEngine.CompileRenderAsync($"{root}{viewPath}", model, viewBag); - } - #endregion - - #region Event Binds - public Regex GetRegex(string regex) => new Regex(regex, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.CultureInvariant); - - public bool TryAddEventCallback(Regex regex, HttpServer.Callback callback) - => Server.TryAddEventCallback(Hostname, regex, callback); - - public void AddEventCallback(Regex regex, HttpServer.Callback callback) { - if (!TryAddEventCallback(regex, callback)) - throw new Exception($"Error in Area '{AreaPath}'! Failed to bind event callback to `{callback.Method.Name}`"); - } - - public bool TryAddEventCallback(string regexPattern, HttpServer.Callback callback) - => TryAddEventCallback(GetRegex(regexPattern), callback); - - public void AddEventCallback(string regexPattern, HttpServer.Callback callback) { - if (!TryAddEventCallback(regexPattern, callback)) - throw new Exception($"Error in Area '{AreaPath}'! Failed to bind event callback to `{callback.Method.Name}`"); - } - #endregion - } -} diff --git a/WebServer/Extensions/ExpandoObject.cs b/WebServer/Extensions/ExpandoObject.cs deleted file mode 100644 index 171cd43..0000000 --- a/WebServer/Extensions/ExpandoObject.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Text; - -namespace WebServer.Extensions { - public static class ExpandoObjectExt { - public static object GetPropertyOrDefault(this ExpandoObject expando, string propertyName, object defaultValue) { - if (expando is IDictionary dict) { - if (!dict.TryGetValue(propertyName, out var result)) - dict.Add(propertyName, result = defaultValue); - return result; - } - return defaultValue; - } - - public static bool TryGetProperty(this ExpandoObject expando, string propertyName, out object value) { - value = null; - return expando is IDictionary dict - && dict.TryGetValue(propertyName, out value); - } - - } -} diff --git a/WebServer/HttpConfiguration.cs b/WebServer/HttpConfiguration.cs deleted file mode 100644 index b6b1049..0000000 --- a/WebServer/HttpConfiguration.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace WebServer { - public class HttpConfiguration { - public bool AutoStart = false; - public ushort Port = 80; - public string DefaultDomain = "localhost"; // The domain that the server will fallback on if the - public bool ShowExceptionOnErrorPages = true; // On InternalServerError(500), should it show the exception? - public ushort MaxConcurrentRequests = 100; - public List UriFillers = new List() { // Todo: Needs revision, - ".html", - ".htm", - ".txt", - "./index.html", - "./index.htm", - "./index.txt", - "./default.webp", - "./default.png", - "../default.webp", - "../default.png" - }; - public Dictionary GenericHeaders = new Dictionary() { - { "x-content-type-options: nosniff", null }, - { "x-xss-protection:1; mode=block", null }, - { "x-frame-options:DENY", null } - }; - public ushort ResponseTimeout = 10; - public uint MaxCacheAge = 604800; - public bool DebugMode = false; // If true, it bypasses cache and also shows exceptions on error page (where applicable) - - public virtual HttpServer CreateServer(IHttpLogger? logger = null) => new HttpServer(this, logger); - } -} diff --git a/WebServer/HttpResponse.cs b/WebServer/HttpResponse.cs deleted file mode 100644 index a951295..0000000 --- a/WebServer/HttpResponse.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Text; -using WebServer.Utils; - -namespace WebServer { - public class HttpResponse { - public HttpStatusCode StatusCode = HttpStatusCode.NoContent; - public bool IsSuccessStatusCode => (int)StatusCode >= 200 && (int)StatusCode <= 299; - public string ContentType = MimeTypeMap.GetMimeType(".txt"); - public byte[] Content = Array.Empty(); - public string ContentString { - get => Encoding.UTF8.GetString(Content); - set => Content = Encoding.UTF8.GetBytes(value); - } - public Dictionary Headers = new Dictionary(); - - public HttpResponse() { } - - public HttpResponse(HttpStatusCode statusCode, byte[] content, string contentType = "text/plain") { - StatusCode = statusCode; - Content = content; - ContentType = contentType; - } - - public HttpResponse(HttpStatusCode statusCode, string content, string contentType = "text/plain") { - StatusCode = statusCode; - ContentString = content; - ContentType = contentType; - } - } -} diff --git a/WebServer/HttpServer.cs b/WebServer/HttpServer.cs deleted file mode 100644 index 712a51c..0000000 --- a/WebServer/HttpServer.cs +++ /dev/null @@ -1,371 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using RazorLight; -using RazorLight.Razor; -using WebServer.Models; -using WebServer.Utils; - -namespace WebServer { - public class HttpServer { - public readonly HttpConfiguration Config; - public readonly DirectoryInfo ViewsDirectory; - public ushort ActivePort { get; protected set; } - public HttpListener HttpListener { get; protected set; } - public IHttpLogger? Logger; - public delegate Task Callback(HttpListenerContext context, CachedResponse? cache); - - Dictionary> HttpCallbacks = new Dictionary>(); - public List RegisteredAreas = new List(); - Dictionary RazorEngines = new Dictionary(); - public readonly RazorLightEngine DefaultRazorEngine; - /* Instead of three directories (from previous render engine) - * - Public/PublicTemplates (was public static stuff, also allowed views) - * - Static (was used for builds) - * - Views/PrivateTemplates (was used for private views) - * There will be only two: - * - Static - For builds, there should also be a build process for this - * - Views - Site source, including public/private views (views are able to be processed) - */ - - public HttpServer(HttpConfiguration config, IHttpLogger? logger = null) { - Config = config; - Logger = logger; - ViewsDirectory = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "Views")); - if (!ViewsDirectory.Exists) - ViewsDirectory.Create(); - if (!string.IsNullOrEmpty(config.DefaultDomain)) - ViewsDirectory.CreateSubdirectory(config.DefaultDomain); - if (config.AutoStart) - _ = StartAsync(); - DefaultRazorEngine = GetOrCreateRazorEngine(config.DefaultDomain); - } - - public RazorLightEngine GetOrCreateRazorEngine(string hostname) { - hostname = hostname.Trim(' ', '/', '\\'); - if (!RazorEngines.TryGetValue(hostname, out RazorLightEngine engine)) { - RazorEngines.Add(hostname, engine = new RazorLightEngineBuilder() - .UseOptions(new RazorLightOptions() { // TODO: make this part of the config - EnableDebugMode = true - }) - .UseProject(new FileSystemRazorProject(Path.Combine(ViewsDirectory.FullName, hostname), ".cshtml")) - .UseMemoryCachingProvider() - .Build() - ); - } - return engine; - } - - public async Task StartAsync() { - if (HttpListener?.IsListening ?? false) - await StopAsync(); - HttpListener = new HttpListener() { - IgnoreWriteExceptions = true // Used to crash the server, don't think it is needed anymore - }; - HttpListener.Prefixes.Add($"http://+:{Config.Port}/"); - - HttpListener.Start(); - ActivePort = Config.Port; - Logger?.Log($"HTTP Service started on port '{ActivePort}'"); - - while (HttpListener.IsListening) - await ListenAsync((ListenToken = new CancellationTokenSource()).Token); - } - - CancellationTokenSource ListenToken; - public int TasksCount => Tasks.Count; - HashSet Tasks = new HashSet(); - async Task ListenAsync(CancellationToken token) { - Tasks = new HashSet(128); - for (int i = 0; i < 64; i++) // Create 64 tasks - Tasks.Add(HttpListener.GetContextAsync()); - Logger?.Log($"Listening with {Tasks.Count} worker(s)"); - while (!token.IsCancellationRequested) { - Task t = await Task.WhenAny(Tasks); - Tasks.Remove(t); - - if (t is Task context) { - if (Tasks.Count < Config.MaxConcurrentRequests) - Tasks.Add(HttpListener.GetContextAsync()); - Tasks.Add(ProcessRequestAsync(context.Result, token)); // Should I really be adding this to tasks? - } - //ProcessRequestAsync triggers this: - // else Logger?.Log($"Got an unexpected task of type '{t.GetType().FullName}'"); - } - } - - public async Task StopAsync() { - HttpListener.Stop(); - ListenToken.Cancel(); - } - - public async Task ProcessRequestAsync(HttpListenerContext context, CancellationToken token) { - List methodsUsed = new List() { MethodBase.GetCurrentMethod() }; - try { - // Add generic headers - foreach (var kvp in Config.GenericHeaders) { - if (kvp.Value is null) - context.Response.Headers.Add(kvp.Key); - else context.Response.Headers.Add(kvp.Key, kvp.Value); - } - - // Get domain and path - string host = (context.Request.Url?.Host ?? Config.DefaultDomain).Trim('/', ' ').ToLowerInvariant(), - callbackKey = FormatCallbackKey(context.Request.Url?.LocalPath ?? string.Empty), - path = callbackKey.Any() ? $"/{callbackKey}" : ""; - string cacheName = $"{host}{path}"; - CachedResponse? cache = CachedResponse.Get(this, cacheName); - HttpResponse? response = null; - if (cache != null) { - if (!cache.NeedsUpdate) - response = cache; - else if (cache.UpdateMethod != null) - response = await cache.UpdateMethod(context, cache); - } - - // Create reponse timeout logic, this will return a string to the client but an exception on the server - CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); - Task cancelationTask = Task.Delay(Timeout.Infinite, cancellationTokenSource.Token); - if (Config.ResponseTimeout > 0) - cancellationTokenSource.CancelAfter(1000 * Config.ResponseTimeout); - - StatusPageModel? statusPageModel = null; - try { - #region Event Callbacks - var hostnames = new[] { host ?? Config.DefaultDomain, Config.DefaultDomain }.Distinct(); - - if (response == null) { - var domainCallbackMatches = hostnames.Where(name => HttpCallbacks.ContainsKey(name)); - var regexCallbacks = domainCallbackMatches.SelectMany(name => HttpCallbacks[name]); - /*var regexCallbacks = new[] { host, DefaultDomain }.Distinct() - .Where(domain => HttpCallbacks.ContainsKey(domain)) - .SelectMany(domain => HttpCallbacks[domain]);*/ - foreach (var kvp in regexCallbacks) { - bool match = kvp.Key.IsMatch(path); - if (!match) continue; - methodsUsed.Add(kvp.Value.Method); - var callback = kvp.Value(context, cache); - var completedTask = await Task.WhenAny(callback, cancelationTask); - // Check that the callback didn't throw any errors (hence it didn't complete) - if (callback.IsFaulted) throw callback.Exception; - // So no faults, check if the completed task is the callback - if (completedTask != callback) - throw new ResponseTimedOutException(cacheName, methodsUsed, cancellationTokenSource.Token); - response = callback.Result; - - if (response != null) break; - } - } - #endregion - - #region Try get Razor File - string fileName = Path.GetFileNameWithoutExtension(path); - bool isPrivateView = fileName.Length > 0 && fileName.StartsWith('_'); - if (response == null && !isPrivateView) { - foreach (var hostname in hostnames) { - // If no razor engine exists, continue to next hostname - if (!RazorEngines.TryGetValue(hostname, out var razorEngine)) - continue; - - string cwd = Path.Combine(ViewsDirectory.FullName, hostname).Replace('\\', '/'); - string[] razorPaths = new[] { $"{path}", $"{path}.cshtml", $"{path}/index.cshtml" }; - foreach (var razorPath in razorPaths) { - string fullpath = cwd + razorPath; - if (Path.GetExtension(fullpath).ToLower() != ".cshtml") continue; - bool fileExists = File.Exists(fullpath); - if (!fileExists) continue; - - cache ??= new CachedResponse(this, null); - cache.StatusCode = HttpStatusCode.OK; - cache.ContentString = await razorEngine.CompileRenderAsync(razorPath, null); - cache.ContentType = "text/html"; - response = cache; - break; - } - } - } - #endregion - - #region Try Static File - // Finally get a static file - if (response == null) { - Callback getStaticFile = GetStaticFile; - methodsUsed.Add(getStaticFile.Method); - response = await getStaticFile(context, cache); - } - #endregion - } - catch (AggregateException ex) { // Also catches OperationCanceledException and ResponseTimedOutException - statusPageModel = new StatusPageModel(HttpStatusCode.ServiceUnavailable) { - Exception = ex - }; - } - catch (Exception ex) { - statusPageModel = new StatusPageModel(HttpStatusCode.InternalServerError) { - Exception = ex - }; - } - - response ??= await GetGenericStatusPageAsync(statusPageModel ?? new StatusPageModel(HttpStatusCode.NotFound)); - - #region Ship Response - context.Response.StatusCode = (int)response.StatusCode; - if (response.StatusCode == HttpStatusCode.Redirect) { - context.Response.Redirect(response.ContentString); - } - foreach (var kvp in response.Headers) { - if (kvp.Value is null) - context.Response.Headers.Add(kvp.Key); - else context.Response.Headers.Add(kvp.Key, kvp.Value); - } - bool omitBody = new[] { "HEAD", "PUT", "DELETE" }.Contains(context.Request.HttpMethod.ToUpper()) || - (100 <= (int)response.StatusCode && (int)response.StatusCode <= 199) || - response.StatusCode == HttpStatusCode.NoContent || - response.StatusCode == HttpStatusCode.NotModified; - if (!omitBody) { - context.Response.Headers["Content-Type"] = response.ContentType; - context.Response.ContentEncoding = Encoding.UTF8; - context.Response.ContentLength64 = response.Content.Length; - await context.Response.OutputStream.WriteAsync(response.Content, 0, response.Content.Length); - } - Logger?.LogRequest(context, response, methodsUsed); - context.Response.Close(); - #endregion - - // And finally, if the response allows cache (by being a cachedResponse) - // override the path that it is on, so it can be reused - if (response is CachedResponse cachedResponse) { - cachedResponse.Path = $"{host}{path}"; // Rewrite the path, so it can be located when a new request is received - if (Config.DebugMode) cachedResponse.RaiseUpdateFlag(); - } - } - catch (Exception ex) { - Logger?.LogError(context, methodsUsed, ex); - } - } - - static string FormatCallbackKey(string key) - => string.IsNullOrEmpty(key) ? string.Empty - : key.ToLower().Replace('\\', '/').Replace("//", "/").Trim(' ', '/'); - - public async Task GetStaticFile(HttpListenerContext context, CachedResponse? cache) => await GetStaticFile(context.Request.Url?.Host, context.Request.Url?.LocalPath, cache); - - public async Task GetStaticFile(string? targetDomain, string? localPath, CachedResponse? cache) { - if (!cache?.NeedsUpdate ?? false) return cache; - string? fileName = Path.GetFileName(localPath); - if (fileName != null && fileName.StartsWith('_') && fileName.EndsWith(".cshtml")) // Is the file a private cshtml file? - localPath = localPath?.Substring(0, localPath.Length - fileName.Length); - DirectoryInfo directory = ViewsDirectory; // Might be changed later - // Works on windows, but on linux, the domain folder will need to be lowercase - targetDomain = targetDomain?.ToLower() ?? Config.DefaultDomain; - string basePath = Path.Combine(directory.FullName, targetDomain); - bool usingFallbackDomain = !Directory.Exists(basePath); - if (usingFallbackDomain) { // Only fallback to default if domain folder doesn't exist - targetDomain = Config.DefaultDomain; - basePath = Path.Combine(directory.FullName, Config.DefaultDomain); - } - string resourceIdentifier = FormatCallbackKey(localPath ?? string.Empty); - CachedResponse resource = cache ?? new CachedResponse(this, null); - string filePath = Path.Combine(basePath, resourceIdentifier); - if (File.Exists(filePath)) { - resource.StatusCode = HttpStatusCode.OK; - resource.ContentType = MimeTypeMap.GetMimeType(Path.GetExtension(filePath).ToLower()); - resource.Content = File.ReadAllBytes(filePath); - resource.Headers["cache-control"] = Config.DebugMode ? "no-store, no-cache, must-revalidate" - : "max-age=360000, s-max-age=900, stale-while-revalidate=120, stale-if-error=86400"; - resource.ClearFlag(); - return resource; - } - string? hitPath = Config.UriFillers.Select(filler => filePath + filler) - .FirstOrDefault(path => path.Contains(basePath) && File.Exists(path)); - if (hitPath != null) { - resource.StatusCode = HttpStatusCode.OK; - resource.ContentType = MimeTypeMap.GetMimeType(Path.GetExtension(hitPath).ToLower()); - resource.Content = File.ReadAllBytes(hitPath); - resource.Headers["cache-control"] = Config.DebugMode ? "no-store, no-cache, must-revalidate" - : "max-age=360000, s-max-age=900, stale-while-revalidate=120, stale-if-error=86400"; - resource.ClearFlag(); - return resource; - } - return await GetGenericStatusPageAsync(new StatusPageModel(Directory.Exists(filePath) ? HttpStatusCode.Forbidden : HttpStatusCode.NotFound), host: targetDomain); - } - - public async Task GetGenericStatusPageAsync(StatusPageModel pageModel, string? host = null, ExpandoObject? viewBag = null) { - //try { pageModel.Exception ??= throw new Exception("test"); } - //catch (Exception e) { pageModel.Exception = e; } - var potentialHosts = new string[] { host ?? Config.DefaultDomain, Config.DefaultDomain }.Distinct() - .Select(h => h.Trim(' ', '/', '\\')); - const string viewName = "_StatusPage.cshtml"; - foreach (string hostname in potentialHosts) { - if (!File.Exists(Path.Combine(ViewsDirectory.FullName, hostname, viewName))) continue; - try { - return new HttpResponse( - pageModel.StatusCode, - await GetOrCreateRazorEngine(hostname).CompileRenderAsync(viewName, pageModel, viewBag), - MimeTypeMap.GetMimeType(".cshtml") - ); - } catch (Exception ex) { - pageModel.Exception = pageModel.Exception != null - ? new AggregateException(pageModel.Exception, ex) - : ex; - break; - } - } - - // If no template was found, fallback to plain text - var bckResponse = new HttpResponse() { - StatusCode = pageModel.StatusCode, - ContentType = MimeTypeMap.GetMimeType(".txt"), - ContentString = $"{(int)pageModel.StatusCode} {pageModel.Header} - {pageModel.Details}\n{pageModel.Exception?.ToString() ?? ""}" - }; - return bckResponse; - } - - public bool ContainsEventCallback(Task callback) { - if (callback == null) return false; - return HttpCallbacks.Any(domainKvp => domainKvp.Value.Values.Any(v => v.Equals(callback))); - } - - public bool TryAddEventCallback(string host, Regex regex, Callback callback) { - if (string.IsNullOrEmpty(host) || regex == null || callback == null) return false; - host = FormatCallbackKey(host); - if (!HttpCallbacks.TryGetValue(host, out var domainCallbacks)) - HttpCallbacks.Add(host, domainCallbacks = new Dictionary()); - domainCallbacks[regex] = callback; - return true; - } - - public bool TryRemoveEventCallback(Callback method) { - if (method == null) return false; - - ushort removeCount = 0; - foreach (var callbackDict in HttpCallbacks.Values) { - foreach (var kvp in callbackDict.Where(kvp => kvp.Value == method)) { - callbackDict.Remove(kvp.Key); - removeCount++; - } - } - return removeCount > 0; - } - - public bool TryRegisterArea(Func? areaInitilizer, out T area) where T : AreaBase { - area = areaInitilizer?.Invoke() ?? default; - if (area == null) return false; - RegisteredAreas.Add(area); - return true; - } - - public void RegisterArea(Func? areaInitializer, out T area) where T : AreaBase { - if (!TryRegisterArea(areaInitializer, out area)) - throw new Exception($"Failed to bind {typeof(T).FullName}! Is your initializer returning a valid area?"); - } - } -} diff --git a/WebServer/IHttpLogger.cs b/WebServer/IHttpLogger.cs deleted file mode 100644 index 4c0aef9..0000000 --- a/WebServer/IHttpLogger.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Reflection; -using System.Text; - -namespace WebServer { - public interface IHttpLogger { - void Log(string message); - void LogRequest(HttpListenerContext context, HttpResponse response, List methodsUsed); - void LogError(HttpListenerContext context, List methodsUsed, Exception exception); - } -} diff --git a/WebServer/Models/StatusPageModel.cs b/WebServer/Models/StatusPageModel.cs deleted file mode 100644 index 15c529c..0000000 --- a/WebServer/Models/StatusPageModel.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Net; -using System.Text; -using System.Threading.Tasks; - -namespace WebServer.Models { - public class StatusPageModel { - public HttpStatusCode StatusCode; - public ushort StatusCodeAsUShort => (ushort)StatusCode; - public string Header; - public string Details; - public Exception? Exception; - - static readonly Dictionary GenericStatusContent = new Dictionary() { - { HttpStatusCode.OK, ("OK", "The request succeeded") }, - { HttpStatusCode.BadRequest, ("Bad Request", "The server cannot or will not process the request due to something that is perceived to be a client error") }, - { HttpStatusCode.Unauthorized, ("Unauthorized", "Authorization Required") }, - { HttpStatusCode.Forbidden, ("Forbidden", "Access to this resource has been revoked") }, - { HttpStatusCode.NotFound, ("Not Found", "Object or Resource not Found") }, - { HttpStatusCode.PreconditionFailed, ("Precondition Failed!", "The server has indicated preconditions which the client does not meet") }, - { HttpStatusCode.InternalServerError, ("Internal Server Error", "The server has encountered a situation it does not know how to handle") }, - { HttpStatusCode.NotImplemented, ("Not Implemented", "he server has encountered a situation it does not know how to handle.") }, - { HttpStatusCode.ServiceUnavailable, ("Service Unavailable", "The server is not ready to handle the request") } - }; - - public StatusPageModel(string title, string subtitle) : this(HttpStatusCode.OK, title, subtitle) {} - - public StatusPageModel(HttpStatusCode statusCode, string? title = null, string? subtitle = null) { - StatusCode = statusCode; - if (!GenericStatusContent.TryGetValue(statusCode, out var tuple)) - tuple = (statusCode.ToString(), "Great! Something happened, not sure what though"); - Header = title ?? tuple.header; - Details = subtitle ?? tuple.details; - } - } -} diff --git a/WebServer/ResponseTimedOutException.cs b/WebServer/ResponseTimedOutException.cs deleted file mode 100644 index f6fb3b9..0000000 --- a/WebServer/ResponseTimedOutException.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Text; -using System.Threading; - -namespace WebServer { - public class ResponseTimedOutException : OperationCanceledException { - public string FullPath; - public List MethodsUsed; - public ResponseTimedOutException(string fullPath, List methodsUsed, CancellationToken token) - : base("The request took too long to fulfil", token) { - FullPath = fullPath; - MethodsUsed = methodsUsed; - } - - public override string ToString() - { - return base.ToString() - + $"\nFullPath: '{FullPath}';" - + $"\nMethods Used: [\n{string.Join(",\n", MethodsUsed.Select(m => $" '{m.ReflectedType.FullName}.{m.Name}'"))} \n];"; - } - } -} diff --git a/WebServer/Utils/CachedResponse.cs b/WebServer/Utils/CachedResponse.cs deleted file mode 100644 index d087a0c..0000000 --- a/WebServer/Utils/CachedResponse.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace WebServer.Utils { - public class CachedResponse : HttpResponse { - #region Static Methods, etc - public static bool BypassCache = false; - public static List Instances { get; private set; } = new List(); - public static ushort RaiseAllUpdateFlags() { - ushort flagsRaised = 0; - foreach (CachedResponse resource in Instances) { - if (resource.NeedsUpdate) continue; - flagsRaised++; - resource.RaiseUpdateFlag(); - //Logger.LogDebug($"Raised Update Flag for '{resource.Name}'"); - } - return flagsRaised; - } - - public static CachedResponse? Get(HttpServer server, string path) { - path = path.Trim(); - return Instances.FirstOrDefault(x => x.Server == server && x.Path == path); - } - - public static bool TryGet(HttpServer server, string path, out CachedResponse? resource) - => (resource = Get(server, path)) != null; - #endregion - - public readonly HttpServer Server; - public string Path; - bool UpdateFlagRaised; - Stopwatch TimeSinceLastUpdate; - public HttpServer.Callback? UpdateMethod; // If null, HttpServer will attempt to find one or GetStaticFile - - public bool NeedsUpdate => UpdateFlagRaised || BypassCache || Server.Config.MaxCacheAge < TimeSinceLastUpdate.Elapsed.TotalSeconds; - - public CachedResponse(HttpServer parentServer, HttpServer.Callback? updateMethod) { - Instances.Add(this); - Server = parentServer; - TimeSinceLastUpdate = Stopwatch.StartNew(); - UpdateMethod = updateMethod; - } - - public void RaiseUpdateFlag() => UpdateFlagRaised = true; - public void ClearFlag() => UpdateFlagRaised = false; - } -} diff --git a/WebServer/Utils/MimeTypeMap.cs b/WebServer/Utils/MimeTypeMap.cs deleted file mode 100644 index 831dcee..0000000 --- a/WebServer/Utils/MimeTypeMap.cs +++ /dev/null @@ -1,829 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace WebServer.Utils -{ - /// - /// Class MimeTypeMap - /// Copied from: https://github.com/samuelneff/MimeTypeMap/blob/master/MimeTypeMap.cs; Commit Id: ec28e2e; Commit Date: May 25, 2022; - /// Referer: https://stackoverflow.com/a/3393525 - /// - public static class MimeTypeMap - { - private const string Dot = "."; - private const string QuestionMark = "?"; - private const string DefaultMimeType = "text/plain"; - private static readonly Lazy> _mappings = new Lazy>(BuildMappings); - public static string HtmlDocument = GetMimeType(".html"); - - private static IDictionary BuildMappings() - { - var mappings = new Dictionary(StringComparer.OrdinalIgnoreCase) { - - #region Big freaking list of mime types - - // maps both ways, - // extension -> mime type - // and - // mime type -> extension - // - // any mime types on left side not pre-loaded on right side, are added automatically - // some mime types can map to multiple extensions, so to get a deterministic mapping, - // add those to the dictionary specifically - // - // combination of values from Windows 7 Registry and - // from C:\Windows\System32\inetsrv\config\applicationHost.config - // some added, including .7z and .dat - // - // Some added based on http://www.iana.org/assignments/media-types/media-types.xhtml - // which lists mime types, but not extensions - // - {".323", "text/h323"}, - {".3g2", "video/3gpp2"}, - {".3gp", "video/3gpp"}, - {".3gp2", "video/3gpp2"}, - {".3gpp", "video/3gpp"}, - {".7z", "application/x-7z-compressed"}, - {".aa", "audio/audible"}, - {".AAC", "audio/aac"}, - {".aaf", "application/octet-stream"}, - {".aax", "audio/vnd.audible.aax"}, - {".ac3", "audio/ac3"}, - {".aca", "application/octet-stream"}, - {".accda", "application/msaccess.addin"}, - {".accdb", "application/msaccess"}, - {".accdc", "application/msaccess.cab"}, - {".accde", "application/msaccess"}, - {".accdr", "application/msaccess.runtime"}, - {".accdt", "application/msaccess"}, - {".accdw", "application/msaccess.webapplication"}, - {".accft", "application/msaccess.ftemplate"}, - {".acx", "application/internet-property-stream"}, - {".AddIn", "text/xml"}, - {".ade", "application/msaccess"}, - {".adobebridge", "application/x-bridge-url"}, - {".adp", "application/msaccess"}, - {".ADT", "audio/vnd.dlna.adts"}, - {".ADTS", "audio/aac"}, - {".afm", "application/octet-stream"}, - {".ai", "application/postscript"}, - {".aif", "audio/aiff"}, - {".aifc", "audio/aiff"}, - {".aiff", "audio/aiff"}, - {".air", "application/vnd.adobe.air-application-installer-package+zip"}, - {".amc", "application/mpeg"}, - {".anx", "application/annodex"}, - {".apk", "application/vnd.android.package-archive"}, - {".apng", "image/apng"}, - {".application", "application/x-ms-application"}, - {".art", "image/x-jg"}, - {".asa", "application/xml"}, - {".asax", "application/xml"}, - {".ascx", "application/xml"}, - {".asd", "application/octet-stream"}, - {".asf", "video/x-ms-asf"}, - {".ashx", "application/xml"}, - {".asi", "application/octet-stream"}, - {".asm", "text/plain"}, - {".asmx", "application/xml"}, - {".aspx", "application/xml"}, - {".asr", "video/x-ms-asf"}, - {".asx", "video/x-ms-asf"}, - {".atom", "application/atom+xml"}, - {".au", "audio/basic"}, - {".avci", "image/avci"}, - {".avcs", "image/avcs"}, - {".avi", "video/x-msvideo"}, - {".avif", "image/avif"}, - {".avifs", "image/avif-sequence"}, - {".axa", "audio/annodex"}, - {".axs", "application/olescript"}, - {".axv", "video/annodex"}, - {".bas", "text/plain"}, - {".bcpio", "application/x-bcpio"}, - {".bin", "application/octet-stream"}, - {".bmp", "image/bmp"}, - {".c", "text/plain"}, - {".cab", "application/octet-stream"}, - {".caf", "audio/x-caf"}, - {".calx", "application/vnd.ms-office.calx"}, - {".cat", "application/vnd.ms-pki.seccat"}, - {".cc", "text/plain"}, - {".cd", "text/plain"}, - {".cdda", "audio/aiff"}, - {".cdf", "application/x-cdf"}, - {".cer", "application/x-x509-ca-cert"}, - {".cfg", "text/plain"}, - {".chm", "application/octet-stream"}, - {".class", "application/x-java-applet"}, - {".clp", "application/x-msclip"}, - {".cmd", "text/plain"}, - {".cmx", "image/x-cmx"}, - {".cnf", "text/plain"}, - {".cod", "image/cis-cod"}, - {".config", "application/xml"}, - {".contact", "text/x-ms-contact"}, - {".coverage", "application/xml"}, - {".cpio", "application/x-cpio"}, - {".cpp", "text/plain"}, - {".crd", "application/x-mscardfile"}, - {".crl", "application/pkix-crl"}, - {".crt", "application/x-x509-ca-cert"}, - {".cs", "text/plain"}, - {".csdproj", "text/plain"}, - {".csh", "application/x-csh"}, - {".csproj", "text/plain"}, - {".css", "text/css"}, - {".csv", "text/csv"}, - {".cur", "application/octet-stream"}, - {".czx", "application/x-czx"}, - {".cxx", "text/plain"}, - {".dat", "application/octet-stream"}, - {".datasource", "application/xml"}, - {".dbproj", "text/plain"}, - {".dcr", "application/x-director"}, - {".def", "text/plain"}, - {".deploy", "application/octet-stream"}, - {".der", "application/x-x509-ca-cert"}, - {".dgml", "application/xml"}, - {".dib", "image/bmp"}, - {".dif", "video/x-dv"}, - {".dir", "application/x-director"}, - {".disco", "text/xml"}, - {".divx", "video/divx"}, - {".dll", "application/x-msdownload"}, - {".dll.config", "text/xml"}, - {".dlm", "text/dlm"}, - {".doc", "application/msword"}, - {".docm", "application/vnd.ms-word.document.macroEnabled.12"}, - {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, - {".dot", "application/msword"}, - {".dotm", "application/vnd.ms-word.template.macroEnabled.12"}, - {".dotx", "application/vnd.openxmlformats-officedocument.wordprocessingml.template"}, - {".dsp", "application/octet-stream"}, - {".dsw", "text/plain"}, - {".dtd", "text/xml"}, - {".dtsConfig", "text/xml"}, - {".dv", "video/x-dv"}, - {".dvi", "application/x-dvi"}, - {".dwf", "drawing/x-dwf"}, - {".dwg", "application/acad"}, - {".dwp", "application/octet-stream"}, - {".dxf", "application/x-dxf"}, - {".dxr", "application/x-director"}, - {".eml", "message/rfc822"}, - {".emf", "image/emf"}, - {".emz", "application/octet-stream"}, - {".eot", "application/vnd.ms-fontobject"}, - {".eps", "application/postscript"}, - {".es", "application/ecmascript"}, - {".etl", "application/etl"}, - {".etx", "text/x-setext"}, - {".evy", "application/envoy"}, - {".exe", "application/vnd.microsoft.portable-executable"}, - {".exe.config", "text/xml"}, - {".f4v", "video/mp4"}, - {".fdf", "application/vnd.fdf"}, - {".fif", "application/fractals"}, - {".filters", "application/xml"}, - {".fla", "application/octet-stream"}, - {".flac", "audio/flac"}, - {".flr", "x-world/x-vrml"}, - {".flv", "video/x-flv"}, - {".fsscript", "application/fsharp-script"}, - {".fsx", "application/fsharp-script"}, - {".generictest", "application/xml"}, - {".geojson", "application/geo+json"}, - {".gif", "image/gif"}, - {".gpx", "application/gpx+xml"}, - {".group", "text/x-ms-group"}, - {".gsm", "audio/x-gsm"}, - {".gtar", "application/x-gtar"}, - {".gz", "application/x-gzip"}, - {".h", "text/plain"}, - {".hdf", "application/x-hdf"}, - {".hdml", "text/x-hdml"}, - {".heic", "image/heic"}, - {".heics", "image/heic-sequence"}, - {".heif", "image/heif"}, - {".heifs", "image/heif-sequence"}, - {".hhc", "application/x-oleobject"}, - {".hhk", "application/octet-stream"}, - {".hhp", "application/octet-stream"}, - {".hlp", "application/winhlp"}, - {".hpp", "text/plain"}, - {".hqx", "application/mac-binhex40"}, - {".hta", "application/hta"}, - {".htc", "text/x-component"}, - {".htm", "text/html"}, - {".html", "text/html"}, - {".cshtml", "text/html"}, - {".htt", "text/webviewhtml"}, - {".hxa", "application/xml"}, - {".hxc", "application/xml"}, - {".hxd", "application/octet-stream"}, - {".hxe", "application/xml"}, - {".hxf", "application/xml"}, - {".hxh", "application/octet-stream"}, - {".hxi", "application/octet-stream"}, - {".hxk", "application/xml"}, - {".hxq", "application/octet-stream"}, - {".hxr", "application/octet-stream"}, - {".hxs", "application/octet-stream"}, - {".hxt", "text/html"}, - {".hxv", "application/xml"}, - {".hxw", "application/octet-stream"}, - {".hxx", "text/plain"}, - {".i", "text/plain"}, - {".ical", "text/calendar"}, - {".icalendar", "text/calendar"}, - {".ico", "image/x-icon"}, - {".ics", "text/calendar"}, - {".idl", "text/plain"}, - {".ief", "image/ief"}, - {".ifb", "text/calendar"}, - {".iii", "application/x-iphone"}, - {".inc", "text/plain"}, - {".inf", "application/octet-stream"}, - {".ini", "text/plain"}, - {".inl", "text/plain"}, - {".ins", "application/x-internet-signup"}, - {".ipa", "application/x-itunes-ipa"}, - {".ipg", "application/x-itunes-ipg"}, - {".ipproj", "text/plain"}, - {".ipsw", "application/x-itunes-ipsw"}, - {".iqy", "text/x-ms-iqy"}, - {".isp", "application/x-internet-signup"}, - {".isma", "application/octet-stream"}, - {".ismv", "application/octet-stream"}, - {".ite", "application/x-itunes-ite"}, - {".itlp", "application/x-itunes-itlp"}, - {".itms", "application/x-itunes-itms"}, - {".itpc", "application/x-itunes-itpc"}, - {".IVF", "video/x-ivf"}, - {".jar", "application/java-archive"}, - {".java", "application/octet-stream"}, - {".jck", "application/liquidmotion"}, - {".jcz", "application/liquidmotion"}, - {".jfif", "image/pjpeg"}, - {".jnlp", "application/x-java-jnlp-file"}, - {".jpb", "application/octet-stream"}, - {".jpe", "image/jpeg"}, - {".jpeg", "image/jpeg"}, - {".jpg", "image/jpeg"}, - {".js", "application/javascript"}, - {".json", "application/json"}, - {".jsx", "text/jscript"}, - {".jsxbin", "text/plain"}, - {".key", "application/vnd.apple.keynote"}, - {".latex", "application/x-latex"}, - {".library-ms", "application/windows-library+xml"}, - {".lit", "application/x-ms-reader"}, - {".loadtest", "application/xml"}, - {".lpk", "application/octet-stream"}, - {".lsf", "video/x-la-asf"}, - {".lst", "text/plain"}, - {".lsx", "video/x-la-asf"}, - {".lzh", "application/octet-stream"}, - {".m13", "application/x-msmediaview"}, - {".m14", "application/x-msmediaview"}, - {".m1v", "video/mpeg"}, - {".m2t", "video/vnd.dlna.mpeg-tts"}, - {".m2ts", "video/vnd.dlna.mpeg-tts"}, - {".m2v", "video/mpeg"}, - {".m3u", "audio/x-mpegurl"}, - {".m3u8", "audio/x-mpegurl"}, - {".m4a", "audio/m4a"}, - {".m4b", "audio/m4b"}, - {".m4p", "audio/m4p"}, - {".m4r", "audio/x-m4r"}, - {".m4v", "video/x-m4v"}, - {".mac", "image/x-macpaint"}, - {".mak", "text/plain"}, - {".man", "application/x-troff-man"}, - {".manifest", "application/x-ms-manifest"}, - {".map", "text/plain"}, - {".master", "application/xml"}, - {".mbox", "application/mbox"}, - {".mda", "application/msaccess"}, - {".mdb", "application/x-msaccess"}, - {".mde", "application/msaccess"}, - {".mdp", "application/octet-stream"}, - {".me", "application/x-troff-me"}, - {".mfp", "application/x-shockwave-flash"}, - {".mht", "message/rfc822"}, - {".mhtml", "message/rfc822"}, - {".mid", "audio/mid"}, - {".midi", "audio/mid"}, - {".mix", "application/octet-stream"}, - {".mk", "text/plain"}, - {".mk3d", "video/x-matroska-3d"}, - {".mka", "audio/x-matroska"}, - {".mkv", "video/x-matroska"}, - {".mmf", "application/x-smaf"}, - {".mno", "text/xml"}, - {".mny", "application/x-msmoney"}, - {".mod", "video/mpeg"}, - {".mov", "video/quicktime"}, - {".movie", "video/x-sgi-movie"}, - {".mp2", "video/mpeg"}, - {".mp2v", "video/mpeg"}, - {".mp3", "audio/mpeg"}, - {".mp4", "video/mp4"}, - {".mp4v", "video/mp4"}, - {".mpa", "video/mpeg"}, - {".mpe", "video/mpeg"}, - {".mpeg", "video/mpeg"}, - {".mpf", "application/vnd.ms-mediapackage"}, - {".mpg", "video/mpeg"}, - {".mpp", "application/vnd.ms-project"}, - {".mpv2", "video/mpeg"}, - {".mqv", "video/quicktime"}, - {".ms", "application/x-troff-ms"}, - {".msg", "application/vnd.ms-outlook"}, - {".msi", "application/octet-stream"}, - {".mso", "application/octet-stream"}, - {".mts", "video/vnd.dlna.mpeg-tts"}, - {".mtx", "application/xml"}, - {".mvb", "application/x-msmediaview"}, - {".mvc", "application/x-miva-compiled"}, - {".mxf", "application/mxf"}, - {".mxp", "application/x-mmxp"}, - {".nc", "application/x-netcdf"}, - {".nsc", "video/x-ms-asf"}, - {".numbers", "application/vnd.apple.numbers"}, - {".nws", "message/rfc822"}, - {".ocx", "application/octet-stream"}, - {".oda", "application/oda"}, - {".odb", "application/vnd.oasis.opendocument.database"}, - {".odc", "application/vnd.oasis.opendocument.chart"}, - {".odf", "application/vnd.oasis.opendocument.formula"}, - {".odg", "application/vnd.oasis.opendocument.graphics"}, - {".odh", "text/plain"}, - {".odi", "application/vnd.oasis.opendocument.image"}, - {".odl", "text/plain"}, - {".odm", "application/vnd.oasis.opendocument.text-master"}, - {".odp", "application/vnd.oasis.opendocument.presentation"}, - {".ods", "application/vnd.oasis.opendocument.spreadsheet"}, - {".odt", "application/vnd.oasis.opendocument.text"}, - {".oga", "audio/ogg"}, - {".ogg", "audio/ogg"}, - {".ogv", "video/ogg"}, - {".ogx", "application/ogg"}, - {".one", "application/onenote"}, - {".onea", "application/onenote"}, - {".onepkg", "application/onenote"}, - {".onetmp", "application/onenote"}, - {".onetoc", "application/onenote"}, - {".onetoc2", "application/onenote"}, - {".opus", "audio/ogg"}, - {".orderedtest", "application/xml"}, - {".osdx", "application/opensearchdescription+xml"}, - {".otf", "application/font-sfnt"}, - {".otg", "application/vnd.oasis.opendocument.graphics-template"}, - {".oth", "application/vnd.oasis.opendocument.text-web"}, - {".otp", "application/vnd.oasis.opendocument.presentation-template"}, - {".ots", "application/vnd.oasis.opendocument.spreadsheet-template"}, - {".ott", "application/vnd.oasis.opendocument.text-template"}, - {".oxps", "application/oxps"}, - {".oxt", "application/vnd.openofficeorg.extension"}, - {".p10", "application/pkcs10"}, - {".p12", "application/x-pkcs12"}, - {".p7b", "application/x-pkcs7-certificates"}, - {".p7c", "application/pkcs7-mime"}, - {".p7m", "application/pkcs7-mime"}, - {".p7r", "application/x-pkcs7-certreqresp"}, - {".p7s", "application/pkcs7-signature"}, - {".pages", "application/vnd.apple.pages"}, - {".pbm", "image/x-portable-bitmap"}, - {".pcast", "application/x-podcast"}, - {".pct", "image/pict"}, - {".pcx", "application/octet-stream"}, - {".pcz", "application/octet-stream"}, - {".pdf", "application/pdf"}, - {".pfb", "application/octet-stream"}, - {".pfm", "application/octet-stream"}, - {".pfx", "application/x-pkcs12"}, - {".pgm", "image/x-portable-graymap"}, - {".pic", "image/pict"}, - {".pict", "image/pict"}, - {".pkgdef", "text/plain"}, - {".pkgundef", "text/plain"}, - {".pko", "application/vnd.ms-pki.pko"}, - {".pls", "audio/scpls"}, - {".pma", "application/x-perfmon"}, - {".pmc", "application/x-perfmon"}, - {".pml", "application/x-perfmon"}, - {".pmr", "application/x-perfmon"}, - {".pmw", "application/x-perfmon"}, - {".png", "image/png"}, - {".pnm", "image/x-portable-anymap"}, - {".pnt", "image/x-macpaint"}, - {".pntg", "image/x-macpaint"}, - {".pnz", "image/png"}, - {".pot", "application/vnd.ms-powerpoint"}, - {".potm", "application/vnd.ms-powerpoint.template.macroEnabled.12"}, - {".potx", "application/vnd.openxmlformats-officedocument.presentationml.template"}, - {".ppa", "application/vnd.ms-powerpoint"}, - {".ppam", "application/vnd.ms-powerpoint.addin.macroEnabled.12"}, - {".ppm", "image/x-portable-pixmap"}, - {".pps", "application/vnd.ms-powerpoint"}, - {".ppsm", "application/vnd.ms-powerpoint.slideshow.macroEnabled.12"}, - {".ppsx", "application/vnd.openxmlformats-officedocument.presentationml.slideshow"}, - {".ppt", "application/vnd.ms-powerpoint"}, - {".pptm", "application/vnd.ms-powerpoint.presentation.macroEnabled.12"}, - {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, - {".prf", "application/pics-rules"}, - {".prm", "application/octet-stream"}, - {".prx", "application/octet-stream"}, - {".ps", "application/postscript"}, - {".psc1", "application/PowerShell"}, - {".psd", "application/octet-stream"}, - {".psess", "application/xml"}, - {".psm", "application/octet-stream"}, - {".psp", "application/octet-stream"}, - {".pst", "application/vnd.ms-outlook"}, - {".pub", "application/x-mspublisher"}, - {".pwz", "application/vnd.ms-powerpoint"}, - {".qht", "text/x-html-insertion"}, - {".qhtm", "text/x-html-insertion"}, - {".qt", "video/quicktime"}, - {".qti", "image/x-quicktime"}, - {".qtif", "image/x-quicktime"}, - {".qtl", "application/x-quicktimeplayer"}, - {".qxd", "application/octet-stream"}, - {".ra", "audio/x-pn-realaudio"}, - {".ram", "audio/x-pn-realaudio"}, - {".rar", "application/x-rar-compressed"}, - {".ras", "image/x-cmu-raster"}, - {".rat", "application/rat-file"}, - {".rc", "text/plain"}, - {".rc2", "text/plain"}, - {".rct", "text/plain"}, - {".rdlc", "application/xml"}, - {".reg", "text/plain"}, - {".resx", "application/xml"}, - {".rf", "image/vnd.rn-realflash"}, - {".rgb", "image/x-rgb"}, - {".rgs", "text/plain"}, - {".rm", "application/vnd.rn-realmedia"}, - {".rmi", "audio/mid"}, - {".rmp", "application/vnd.rn-rn_music_package"}, - {".rmvb", "application/vnd.rn-realmedia-vbr"}, - {".roff", "application/x-troff"}, - {".rpm", "audio/x-pn-realaudio-plugin"}, - {".rqy", "text/x-ms-rqy"}, - {".rtf", "application/rtf"}, - {".rtx", "text/richtext"}, - {".rvt", "application/octet-stream"}, - {".ruleset", "application/xml"}, - {".s", "text/plain"}, - {".safariextz", "application/x-safari-safariextz"}, - {".scd", "application/x-msschedule"}, - {".scr", "text/plain"}, - {".sct", "text/scriptlet"}, - {".sd2", "audio/x-sd2"}, - {".sdp", "application/sdp"}, - {".sea", "application/octet-stream"}, - {".searchConnector-ms", "application/windows-search-connector+xml"}, - {".setpay", "application/set-payment-initiation"}, - {".setreg", "application/set-registration-initiation"}, - {".settings", "application/xml"}, - {".sgimb", "application/x-sgimb"}, - {".sgml", "text/sgml"}, - {".sh", "application/x-sh"}, - {".shar", "application/x-shar"}, - {".shtml", "text/html"}, - {".sit", "application/x-stuffit"}, - {".sitemap", "application/xml"}, - {".skin", "application/xml"}, - {".skp", "application/x-koan"}, - {".sldm", "application/vnd.ms-powerpoint.slide.macroEnabled.12"}, - {".sldx", "application/vnd.openxmlformats-officedocument.presentationml.slide"}, - {".slk", "application/vnd.ms-excel"}, - {".sln", "text/plain"}, - {".slupkg-ms", "application/x-ms-license"}, - {".smd", "audio/x-smd"}, - {".smi", "application/octet-stream"}, - {".smx", "audio/x-smd"}, - {".smz", "audio/x-smd"}, - {".snd", "audio/basic"}, - {".snippet", "application/xml"}, - {".snp", "application/octet-stream"}, - {".sql", "application/sql"}, - {".sol", "text/plain"}, - {".sor", "text/plain"}, - {".spc", "application/x-pkcs7-certificates"}, - {".spl", "application/futuresplash"}, - {".spx", "audio/ogg"}, - {".src", "application/x-wais-source"}, - {".srf", "text/plain"}, - {".SSISDeploymentManifest", "text/xml"}, - {".ssm", "application/streamingmedia"}, - {".sst", "application/vnd.ms-pki.certstore"}, - {".stl", "application/vnd.ms-pki.stl"}, - {".sv4cpio", "application/x-sv4cpio"}, - {".sv4crc", "application/x-sv4crc"}, - {".svc", "application/xml"}, - {".svg", "image/svg+xml"}, - {".swf", "application/x-shockwave-flash"}, - {".step", "application/step"}, - {".stp", "application/step"}, - {".t", "application/x-troff"}, - {".tar", "application/x-tar"}, - {".tcl", "application/x-tcl"}, - {".testrunconfig", "application/xml"}, - {".testsettings", "application/xml"}, - {".tex", "application/x-tex"}, - {".texi", "application/x-texinfo"}, - {".texinfo", "application/x-texinfo"}, - {".tgz", "application/x-compressed"}, - {".thmx", "application/vnd.ms-officetheme"}, - {".thn", "application/octet-stream"}, - {".tif", "image/tiff"}, - {".tiff", "image/tiff"}, - {".tlh", "text/plain"}, - {".tli", "text/plain"}, - {".toc", "application/octet-stream"}, - {".tr", "application/x-troff"}, - {".trm", "application/x-msterminal"}, - {".trx", "application/xml"}, - {".ts", "video/vnd.dlna.mpeg-tts"}, - {".tsv", "text/tab-separated-values"}, - {".ttf", "application/font-sfnt"}, - {".tts", "video/vnd.dlna.mpeg-tts"}, - {".txt", "text/plain"}, - {".u32", "application/octet-stream"}, - {".uls", "text/iuls"}, - {".user", "text/plain"}, - {".ustar", "application/x-ustar"}, - {".vb", "text/plain"}, - {".vbdproj", "text/plain"}, - {".vbk", "video/mpeg"}, - {".vbproj", "text/plain"}, - {".vbs", "text/vbscript"}, - {".vcf", "text/x-vcard"}, - {".vcproj", "application/xml"}, - {".vcs", "text/plain"}, - {".vcxproj", "application/xml"}, - {".vddproj", "text/plain"}, - {".vdp", "text/plain"}, - {".vdproj", "text/plain"}, - {".vdx", "application/vnd.ms-visio.viewer"}, - {".vml", "text/xml"}, - {".vscontent", "application/xml"}, - {".vsct", "text/xml"}, - {".vsd", "application/vnd.visio"}, - {".vsi", "application/ms-vsi"}, - {".vsix", "application/vsix"}, - {".vsixlangpack", "text/xml"}, - {".vsixmanifest", "text/xml"}, - {".vsmdi", "application/xml"}, - {".vspscc", "text/plain"}, - {".vss", "application/vnd.visio"}, - {".vsscc", "text/plain"}, - {".vssettings", "text/xml"}, - {".vssscc", "text/plain"}, - {".vst", "application/vnd.visio"}, - {".vstemplate", "text/xml"}, - {".vsto", "application/x-ms-vsto"}, - {".vsw", "application/vnd.visio"}, - {".vsx", "application/vnd.visio"}, - {".vtt", "text/vtt"}, - {".vtx", "application/vnd.visio"}, - {".wasm", "application/wasm"}, - {".wav", "audio/wav"}, - {".wave", "audio/wav"}, - {".wax", "audio/x-ms-wax"}, - {".wbk", "application/msword"}, - {".wbmp", "image/vnd.wap.wbmp"}, - {".wcm", "application/vnd.ms-works"}, - {".wdb", "application/vnd.ms-works"}, - {".wdp", "image/vnd.ms-photo"}, - {".webarchive", "application/x-safari-webarchive"}, - {".webm", "video/webm"}, - {".webp", "image/webp"}, /* https://en.wikipedia.org/wiki/WebP */ - {".webtest", "application/xml"}, - {".wiq", "application/xml"}, - {".wiz", "application/msword"}, - {".wks", "application/vnd.ms-works"}, - {".WLMP", "application/wlmoviemaker"}, - {".wlpginstall", "application/x-wlpg-detect"}, - {".wlpginstall3", "application/x-wlpg3-detect"}, - {".wm", "video/x-ms-wm"}, - {".wma", "audio/x-ms-wma"}, - {".wmd", "application/x-ms-wmd"}, - {".wmf", "application/x-msmetafile"}, - {".wml", "text/vnd.wap.wml"}, - {".wmlc", "application/vnd.wap.wmlc"}, - {".wmls", "text/vnd.wap.wmlscript"}, - {".wmlsc", "application/vnd.wap.wmlscriptc"}, - {".wmp", "video/x-ms-wmp"}, - {".wmv", "video/x-ms-wmv"}, - {".wmx", "video/x-ms-wmx"}, - {".wmz", "application/x-ms-wmz"}, - {".woff", "application/font-woff"}, - {".woff2", "application/font-woff2"}, - {".wpl", "application/vnd.ms-wpl"}, - {".wps", "application/vnd.ms-works"}, - {".wri", "application/x-mswrite"}, - {".wrl", "x-world/x-vrml"}, - {".wrz", "x-world/x-vrml"}, - {".wsc", "text/scriptlet"}, - {".wsdl", "text/xml"}, - {".wvx", "video/x-ms-wvx"}, - {".x", "application/directx"}, - {".xaf", "x-world/x-vrml"}, - {".xaml", "application/xaml+xml"}, - {".xap", "application/x-silverlight-app"}, - {".xbap", "application/x-ms-xbap"}, - {".xbm", "image/x-xbitmap"}, - {".xdr", "text/plain"}, - {".xht", "application/xhtml+xml"}, - {".xhtml", "application/xhtml+xml"}, - {".xla", "application/vnd.ms-excel"}, - {".xlam", "application/vnd.ms-excel.addin.macroEnabled.12"}, - {".xlc", "application/vnd.ms-excel"}, - {".xld", "application/vnd.ms-excel"}, - {".xlk", "application/vnd.ms-excel"}, - {".xll", "application/vnd.ms-excel"}, - {".xlm", "application/vnd.ms-excel"}, - {".xls", "application/vnd.ms-excel"}, - {".xlsb", "application/vnd.ms-excel.sheet.binary.macroEnabled.12"}, - {".xlsm", "application/vnd.ms-excel.sheet.macroEnabled.12"}, - {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, - {".xlt", "application/vnd.ms-excel"}, - {".xltm", "application/vnd.ms-excel.template.macroEnabled.12"}, - {".xltx", "application/vnd.openxmlformats-officedocument.spreadsheetml.template"}, - {".xlw", "application/vnd.ms-excel"}, - {".xml", "text/xml"}, - {".xmp", "application/octet-stream"}, - {".xmta", "application/xml"}, - {".xof", "x-world/x-vrml"}, - {".XOML", "text/plain"}, - {".xpm", "image/x-xpixmap"}, - {".xps", "application/vnd.ms-xpsdocument"}, - {".xrm-ms", "text/xml"}, - {".xsc", "application/xml"}, - {".xsd", "text/xml"}, - {".xsf", "text/xml"}, - {".xsl", "text/xml"}, - {".xslt", "text/xml"}, - {".xsn", "application/octet-stream"}, - {".xss", "application/xml"}, - {".xspf", "application/xspf+xml"}, - {".xtp", "application/octet-stream"}, - {".xwd", "image/x-xwindowdump"}, - {".z", "application/x-compress"}, - {".zip", "application/zip"}, - - {"application/fsharp-script", ".fsx"}, - {"application/msaccess", ".adp"}, - {"application/msword", ".doc"}, - {"application/octet-stream", ".bin"}, - {"application/onenote", ".one"}, - {"application/postscript", ".eps"}, - {"application/step", ".step"}, - {"application/vnd.apple.keynote", ".key"}, - {"application/vnd.apple.numbers", ".numbers"}, - {"application/vnd.apple.pages", ".pages"}, - {"application/vnd.ms-excel", ".xls"}, - {"application/vnd.ms-powerpoint", ".ppt"}, - {"application/vnd.ms-works", ".wks"}, - {"application/vnd.visio", ".vsd"}, - {"application/x-director", ".dir"}, - {"application/x-msdos-program", ".exe"}, - {"application/x-shockwave-flash", ".swf"}, - {"application/x-x509-ca-cert", ".cer"}, - {"application/x-zip-compressed", ".zip"}, - {"application/xhtml+xml", ".xhtml"}, - {"application/x-iwork-keynote-sffkey", ".key"}, - {"application/x-iwork-numbers-sffnumbers", ".numbers"}, - {"application/x-iwork-pages-sffpages", ".pages"}, - {"application/xml", ".xml"}, // anomaly, .xml -> text/xml, but application/xml -> many things, but all are xml, so safest is .xml - {"audio/aac", ".AAC"}, - {"audio/aiff", ".aiff"}, - {"audio/basic", ".snd"}, - {"audio/mid", ".midi"}, - {"audio/mp4", ".m4a"}, // one way mapping only, mime -> ext - {"audio/ogg", ".ogg"}, - {"audio/ogg; codecs=opus", ".opus"}, - {"audio/wav", ".wav"}, - {"audio/x-m4a", ".m4a"}, - {"audio/x-mpegurl", ".m3u"}, - {"audio/x-pn-realaudio", ".ra"}, - {"audio/x-smd", ".smd"}, - {"image/bmp", ".bmp"}, - {"image/heic", ".heic"}, - {"image/heif", ".heif"}, - {"image/jpeg", ".jpg"}, - {"image/pict", ".pic"}, - {"image/png", ".png"}, // Defined in [RFC-2045], [RFC-2048] - {"image/x-png", ".png"}, // See https://www.w3.org/TR/PNG/#A-Media-type :"It is recommended that implementations also recognize the media type "image/x-png"." - {"image/tiff", ".tiff"}, - {"image/x-macpaint", ".mac"}, - {"image/x-quicktime", ".qti"}, - {"message/rfc822", ".eml"}, - {"text/calendar", ".ics"}, - {"text/html", ".html"}, - {"text/plain", ".txt"}, - {"text/scriptlet", ".wsc"}, - {"text/xml", ".xml"}, - {"video/3gpp", ".3gp"}, - {"video/3gpp2", ".3gp2"}, - {"video/mp4", ".mp4"}, - {"video/mpeg", ".mpg"}, - {"video/quicktime", ".mov"}, - {"video/vnd.dlna.mpeg-tts", ".m2t"}, - {"video/x-dv", ".dv"}, - {"video/x-la-asf", ".lsf"}, - {"video/x-ms-asf", ".asf"}, - {"x-world/x-vrml", ".xof"}, - - #endregion - - }; - - var cache = mappings.ToList(); // need ToList() to avoid modifying while still enumerating - - foreach (var mapping in cache) - { - if (!mappings.ContainsKey(mapping.Value)) - { - mappings.Add(mapping.Value, mapping.Key); - } - } - - return mappings; - } - - /// - /// Tries to get the type of the MIME from the provided string. - /// - /// The filename or extension. - /// The variable to store the MIME type. - /// The MIME type. - /// - public static bool TryGetMimeType(string str, out string mimeType) - { - if (str == null) throw new ArgumentNullException(nameof(str)); - - var indexQuestionMark = str.IndexOf(QuestionMark, StringComparison.Ordinal); - if (indexQuestionMark != -1) - { - str = str.Remove(indexQuestionMark); - } - - - if (!str.StartsWith(Dot)) - { - var index = str.LastIndexOf(Dot); - if (index != -1 && str.Length > index + 1) - { - str = str.Substring(index + 1); - } - - str = Dot + str; - } - - return _mappings.Value.TryGetValue(str, out mimeType); - } - - /// - /// Gets the type of the MIME from the provided string. - /// - /// The filename or extension. - /// The MIME type. - /// - public static string GetMimeType(string str) - { - return TryGetMimeType(str, out var result) ? result : DefaultMimeType; - } - - /// - /// Gets the extension from the provided MINE type. - /// - /// Type of the MIME. - /// if set to true, throws error if extension's not found. - /// The extension. - /// - /// - public static string GetExtension(string mimeType, bool throwErrorIfNotFound = true) - { - if (mimeType == null) - throw new ArgumentNullException(nameof(mimeType)); - - if (mimeType.StartsWith(Dot)) - throw new ArgumentException("Requested mime type is not valid: " + mimeType); - - if (_mappings.Value.TryGetValue(mimeType, out string extension)) - return extension; - - if (throwErrorIfNotFound) - throw new ArgumentException("Requested mime type is not registered: " + mimeType); - - return string.Empty; - } - } -} \ No newline at end of file diff --git a/WebServer/WebServer.csproj b/WebServer/WebServer.csproj deleted file mode 100644 index e2b21ef..0000000 --- a/WebServer/WebServer.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - netstandard2.1 - enable - true - - - - - - -