From 03e661a777bfa18a7cebffc70d40716d0a68e231 Mon Sep 17 00:00:00 2001 From: timerix Date: Thu, 9 Nov 2023 14:04:49 +0600 Subject: [PATCH] initial commit --- .gitignore | 5 +++ Fluzm.sln | 16 +++++++++ Fluzm/Config.cs | 43 +++++++++++++++++++++++ Fluzm/Fluzm.csproj | 16 +++++++++ Fluzm/Program.cs | 85 ++++++++++++++++++++++++++++++++++++++++++++++ Fluzm/Router.cs | 35 +++++++++++++++++++ 6 files changed, 200 insertions(+) create mode 100644 .gitignore create mode 100644 Fluzm.sln create mode 100644 Fluzm/Config.cs create mode 100644 Fluzm/Fluzm.csproj create mode 100644 Fluzm/Program.cs create mode 100644 Fluzm/Router.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68666b5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bin/ +obj/ +.idea/ +log/ +logs/ diff --git a/Fluzm.sln b/Fluzm.sln new file mode 100644 index 0000000..92ddc7d --- /dev/null +++ b/Fluzm.sln @@ -0,0 +1,16 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluzm", "Fluzm\Fluzm.csproj", "{7AEF72D2-CFE1-4A6C-B5E0-A4F9386F94CF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7AEF72D2-CFE1-4A6C-B5E0-A4F9386F94CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AEF72D2-CFE1-4A6C-B5E0-A4F9386F94CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AEF72D2-CFE1-4A6C-B5E0-A4F9386F94CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AEF72D2-CFE1-4A6C-B5E0-A4F9386F94CF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/Fluzm/Config.cs b/Fluzm/Config.cs new file mode 100644 index 0000000..097772b --- /dev/null +++ b/Fluzm/Config.cs @@ -0,0 +1,43 @@ +using System; +using DTLib.Dtsod; + +namespace Fluzm; + +public class Config +{ + public const int ActualVersion = 1; + + public int Version = ActualVersion; + public string Address = "127.0.0.1"; + public int Port = 8080; + + public Config() + { } + + public Config(int version, string address, int port) + { + Version = version; + Address = address; + Port = port; + } + + public static Config FromDtsod(DtsodV23 d) + { + var cfg = new Config(d["version"], d["address"], d["port"]); + if (cfg.Version < ActualVersion) + throw new Exception($"config is obsolete (config v{cfg.Version} < program v{ActualVersion})"); + if(cfg.Version > ActualVersion) + throw new Exception($"program is obsolete (config v{cfg.Version} > program v{ActualVersion})"); + return cfg; + } + + public DtsodV23 ToDtsod() => + new() + { + { "version", Version }, + { "address", Address }, + { "port", Port } + }; + + public override string ToString() => ToDtsod().ToString()!; +} \ No newline at end of file diff --git a/Fluzm/Fluzm.csproj b/Fluzm/Fluzm.csproj new file mode 100644 index 0000000..2b0c482 --- /dev/null +++ b/Fluzm/Fluzm.csproj @@ -0,0 +1,16 @@ + + + + Exe + net7.0 + disable + enable + + + + + + + + + diff --git a/Fluzm/Program.cs b/Fluzm/Program.cs new file mode 100644 index 0000000..8a70536 --- /dev/null +++ b/Fluzm/Program.cs @@ -0,0 +1,85 @@ +global using System; +global using System.Collections.Generic; +global using System.Net; +global using System.Text; +global using System.Threading; +global using System.Threading.Tasks; +global using DTLib.Filesystem; +global using DTLib.Extensions; +using DTLib.Dtsod; +using DTLib.Logging; + +namespace Fluzm; + +internal static class Program +{ + private static readonly IOPath configPath = "./config.dtsod"; + private static CancellationTokenSource mainCancel = new(); + private static ILogger logger = new ConsoleLogger(); + private static Router router = new Router(); + + public static async Task Main(string[] args) + { + try + { + Console.InputEncoding = Encoding.UTF8; + Console.OutputEncoding = Encoding.UTF8; + Console.CancelKeyPress += (_, _) => + { + logger.LogInfo("Main","stopping server..."); + mainCancel.Cancel(); + }; + + Config config; + if (!File.Exists(configPath)) + { + logger.LogWarn("Main", "Config file not found."); + config = new Config(); + File.WriteAllText(configPath, config.ToString()); + logger.LogWarn("Main", $"Created default at {configPath}."); + } + else config = Config.FromDtsod(new DtsodV23(File.ReadAllText(configPath))); + + string baseUrl = $"http://{config.Address}:{config.Port}/"; + logger.LogInfo("Main", $"starting webserver at {baseUrl} ..."); + var server = new HttpListener(); + server.Prefixes.Add(baseUrl); + server.Start(); + logger.LogInfo("Main", "server started"); + long requestId = 0; + while (!mainCancel.IsCancellationRequested) + { + await Task.Run(async () => + { + var ctx = await server.GetContextAsync(); + HandleRequestAsync(ctx, requestId); + }, mainCancel.Token); + requestId++; + } + + server.Stop(); + logger.LogInfo("Main", "server stopped"); + } + catch (Exception ex) + { + logger.LogError("Main", ex); + } + } + + private static async void HandleRequestAsync(HttpListenerContext ctx, long requestId) + { + await Task.Yield(); + string logContext = "Request " + requestId; + try + { + logger.LogInfo(logContext, $"{ctx.Request.HttpMethod} request for {ctx.Request.RawUrl} from {ctx.Request.RemoteEndPoint}"); + if(router.TryResolve(ctx)) + logger.LogInfo(logContext, "request handled"); + else logger.LogInfo(logContext, "request rejected"); + } + catch (Exception ex) + { + logger.LogWarn(logContext, ex); + } + } +} \ No newline at end of file diff --git a/Fluzm/Router.cs b/Fluzm/Router.cs new file mode 100644 index 0000000..1444c50 --- /dev/null +++ b/Fluzm/Router.cs @@ -0,0 +1,35 @@ +namespace Fluzm; + +public class Router +{ + private Dictionary> routes = new(); + private Func rejectDelegate; + + public Router() + { + routes.Add("/", ctx => + { + var buffer = "

UwU

".ToBytes(); + ctx.Response.ContentLength64 = buffer.Length; + ctx.Response.OutputStream.Write(buffer); + ctx.Response.OutputStream.Flush(); + return 200; + }); + rejectDelegate = ctx => + { + ctx.Response.ContentLength64 = 0; + return 500; + }; + } + + public bool TryResolve(HttpListenerContext ctx) + { + int status = routes.TryGetValue(ctx.Request.Url!.AbsolutePath, out var routeDelegate) + ? routeDelegate(ctx) + : rejectDelegate(ctx); + ctx.Response.StatusCode = status; + ctx.Response.OutputStream.Flush(); + ctx.Response.OutputStream.Close(); + return status == 200; + } +} \ No newline at end of file