moved *.cs to src/
This commit is contained in:
parent
e37be491ba
commit
9cd059bad2
@ -1,43 +1,40 @@
|
|||||||
using System;
|
namespace Fluzm;
|
||||||
using DTLib.Dtsod;
|
|
||||||
|
public class Config
|
||||||
namespace Fluzm;
|
{
|
||||||
|
public const int ActualVersion = 1;
|
||||||
public class Config
|
|
||||||
{
|
public int Version = ActualVersion;
|
||||||
public const int ActualVersion = 1;
|
public string Address = "127.0.0.1";
|
||||||
|
public int Port = 8080;
|
||||||
public int Version = ActualVersion;
|
|
||||||
public string Address = "127.0.0.1";
|
public Config()
|
||||||
public int Port = 8080;
|
{ }
|
||||||
|
|
||||||
public Config()
|
public Config(int version, string address, int port)
|
||||||
{ }
|
{
|
||||||
|
Version = version;
|
||||||
public Config(int version, string address, int port)
|
Address = address;
|
||||||
{
|
Port = port;
|
||||||
Version = version;
|
}
|
||||||
Address = address;
|
|
||||||
Port = port;
|
public static Config FromDtsod(DtsodV23 d)
|
||||||
}
|
{
|
||||||
|
var cfg = new Config(d["version"], d["address"], d["port"]);
|
||||||
public static Config FromDtsod(DtsodV23 d)
|
if (cfg.Version < ActualVersion)
|
||||||
{
|
throw new Exception($"config is obsolete (config v{cfg.Version} < program v{ActualVersion})");
|
||||||
var cfg = new Config(d["version"], d["address"], d["port"]);
|
if(cfg.Version > ActualVersion)
|
||||||
if (cfg.Version < ActualVersion)
|
throw new Exception($"program is obsolete (config v{cfg.Version} > program v{ActualVersion})");
|
||||||
throw new Exception($"config is obsolete (config v{cfg.Version} < program v{ActualVersion})");
|
return cfg;
|
||||||
if(cfg.Version > ActualVersion)
|
}
|
||||||
throw new Exception($"program is obsolete (config v{cfg.Version} > program v{ActualVersion})");
|
|
||||||
return cfg;
|
public DtsodV23 ToDtsod() =>
|
||||||
}
|
new()
|
||||||
|
{
|
||||||
public DtsodV23 ToDtsod() =>
|
{ "version", Version },
|
||||||
new()
|
{ "address", Address },
|
||||||
{
|
{ "port", Port }
|
||||||
{ "version", Version },
|
};
|
||||||
{ "address", Address },
|
|
||||||
{ "port", Port }
|
public override string ToString() => ToDtsod().ToString()!;
|
||||||
};
|
|
||||||
|
|
||||||
public override string ToString() => ToDtsod().ToString()!;
|
|
||||||
}
|
}
|
||||||
@ -1,84 +1,84 @@
|
|||||||
global using System;
|
global using System;
|
||||||
global using System.Collections.Generic;
|
global using System.Collections.Generic;
|
||||||
global using System.Net;
|
global using System.Net;
|
||||||
global using System.Text;
|
global using System.Text;
|
||||||
global using System.Threading;
|
global using System.Threading;
|
||||||
global using System.Threading.Tasks;
|
global using System.Threading.Tasks;
|
||||||
global using DTLib.Filesystem;
|
global using DTLib.Filesystem;
|
||||||
global using DTLib.Extensions;
|
global using DTLib.Extensions;
|
||||||
using DTLib.Dtsod;
|
global using DTLib.Dtsod;
|
||||||
using DTLib.Logging;
|
using DTLib.Logging;
|
||||||
|
|
||||||
namespace Fluzm;
|
namespace Fluzm;
|
||||||
|
|
||||||
internal static class Program
|
internal static class Program
|
||||||
{
|
{
|
||||||
private static readonly IOPath configPath = "./config.dtsod";
|
private static readonly IOPath configPath = "./config.dtsod";
|
||||||
private static CancellationTokenSource mainCancel = new();
|
private static CancellationTokenSource mainCancel = new();
|
||||||
private static ILogger logger = new ConsoleLogger();
|
private static ILogger logger = new ConsoleLogger();
|
||||||
private static Router router = new Router();
|
private static Router router = new Router();
|
||||||
|
|
||||||
public static async Task Main(string[] args)
|
public static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Console.InputEncoding = Encoding.UTF8;
|
Console.InputEncoding = Encoding.UTF8;
|
||||||
Console.OutputEncoding = Encoding.UTF8;
|
Console.OutputEncoding = Encoding.UTF8;
|
||||||
Console.CancelKeyPress += (_, _) =>
|
Console.CancelKeyPress += (_, _) =>
|
||||||
{
|
{
|
||||||
logger.LogInfo("Main","stopping server...");
|
logger.LogInfo("Main","stopping server...");
|
||||||
mainCancel.Cancel();
|
mainCancel.Cancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
Config config;
|
Config config;
|
||||||
if (!File.Exists(configPath))
|
if (!File.Exists(configPath))
|
||||||
{
|
{
|
||||||
logger.LogWarn("Main", "Config file not found.");
|
logger.LogWarn("Main", "Config file not found.");
|
||||||
config = new Config();
|
config = new Config();
|
||||||
File.WriteAllText(configPath, config.ToString());
|
File.WriteAllText(configPath, config.ToString());
|
||||||
logger.LogWarn("Main", $"Created default at {configPath}.");
|
logger.LogWarn("Main", $"Created default at {configPath}.");
|
||||||
}
|
}
|
||||||
else config = Config.FromDtsod(new DtsodV23(File.ReadAllText(configPath)));
|
else config = Config.FromDtsod(new DtsodV23(File.ReadAllText(configPath)));
|
||||||
|
|
||||||
string baseUrl = $"http://{config.Address}:{config.Port}/";
|
string baseUrl = $"http://{config.Address}:{config.Port}/";
|
||||||
logger.LogInfo("Main", $"starting webserver at {baseUrl} ...");
|
logger.LogInfo("Main", $"starting webserver at {baseUrl} ...");
|
||||||
var server = new HttpListener();
|
var server = new HttpListener();
|
||||||
server.Prefixes.Add(baseUrl);
|
server.Prefixes.Add(baseUrl);
|
||||||
server.Start();
|
server.Start();
|
||||||
logger.LogInfo("Main", "server started");
|
logger.LogInfo("Main", "server started");
|
||||||
long requestId = 0;
|
long requestId = 0;
|
||||||
while (!mainCancel.IsCancellationRequested)
|
while (!mainCancel.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
await Task.Run(async () =>
|
await Task.Run(async () =>
|
||||||
{
|
{
|
||||||
var ctx = await server.GetContextAsync();
|
var ctx = await server.GetContextAsync();
|
||||||
HandleRequestAsync(ctx, requestId);
|
HandleRequestAsync(ctx, requestId);
|
||||||
}, mainCancel.Token);
|
}, mainCancel.Token);
|
||||||
requestId++;
|
requestId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
server.Stop();
|
server.Stop();
|
||||||
logger.LogInfo("Main", "server stopped");
|
logger.LogInfo("Main", "server stopped");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogError("Main", ex);
|
logger.LogError("Main", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async void HandleRequestAsync(HttpListenerContext ctx, long requestId)
|
private static async void HandleRequestAsync(HttpListenerContext ctx, long requestId)
|
||||||
{
|
{
|
||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
string logContext = "Request " + requestId;
|
string logContext = "Request " + requestId;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
logger.LogInfo(logContext, $"{ctx.Request.HttpMethod} request for {ctx.Request.RawUrl} from {ctx.Request.RemoteEndPoint}");
|
logger.LogInfo(logContext, $"{ctx.Request.HttpMethod} request for {ctx.Request.RawUrl} from {ctx.Request.RemoteEndPoint}");
|
||||||
bool isRequestHandled = router.TryResolve(ctx);
|
bool isRequestHandled = router.TryResolve(ctx);
|
||||||
logger.LogInfo(logContext, isRequestHandled ? "request handled" : "request rejected");
|
logger.LogInfo(logContext, isRequestHandled ? "request handled" : "request rejected");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogWarn(logContext, ex);
|
logger.LogWarn(logContext, ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,95 +1,101 @@
|
|||||||
namespace Fluzm;
|
namespace Fluzm;
|
||||||
|
|
||||||
public class Router
|
public class Router
|
||||||
{
|
{
|
||||||
private Dictionary<string, Func<HttpListenerContext, int>> routes = new();
|
private Dictionary<string, Func<HttpListenerContext, int>> routes = new();
|
||||||
private readonly IOPath publicDirPath = "./public";
|
private readonly IOPath publicDirPath = "./public";
|
||||||
private readonly IOPath homepagePath = "./public/html/index.html";
|
private readonly IOPath homepagePath = "./public/html/index.html";
|
||||||
private readonly IOPath pare404Path = "./public/html/404.html";
|
private readonly IOPath pare404Path = "./public/html/404.html";
|
||||||
|
|
||||||
public Router()
|
public Router()
|
||||||
{
|
{
|
||||||
routes.Add("UwU", ctx =>
|
routes.Add("/aaa", ctx =>
|
||||||
{
|
{
|
||||||
var buffer = "<html><body><h1>UwU</h1></body></html>".ToBytes();
|
var buffer = """
|
||||||
ctx.Response.ContentLength64 = buffer.Length;
|
<html>
|
||||||
ctx.Response.OutputStream.Write(buffer);
|
<body>
|
||||||
ctx.Response.OutputStream.Flush();
|
<h1>aaa</h1>
|
||||||
return 200;
|
</body>
|
||||||
});
|
</html>
|
||||||
}
|
""".ToBytes();
|
||||||
|
ctx.Response.ContentLength64 = buffer.Length;
|
||||||
public bool TryResolve(HttpListenerContext ctx)
|
ctx.Response.OutputStream.Write(buffer);
|
||||||
{
|
return 200;
|
||||||
int status;
|
});
|
||||||
if (routes.TryGetValue(ctx.Request.Url!.AbsolutePath, out var routeDelegate))
|
}
|
||||||
status = routeDelegate(ctx);
|
|
||||||
else if(ctx.Request.HttpMethod == "GET")
|
public bool TryResolve(HttpListenerContext ctx)
|
||||||
{
|
{
|
||||||
string urlPath = ctx.Request.Url.AbsolutePath;
|
int status;
|
||||||
string ext = urlPath.EndsWith(".js.map")
|
if (routes.TryGetValue(ctx.Request.Url!.AbsolutePath, out var routeDelegate))
|
||||||
? ".js.map" // extname of '*.js.map' is '.map' so it has to be set explicitely
|
status = routeDelegate(ctx);
|
||||||
: Path.Extension(urlPath).Str;
|
else if(ctx.Request.HttpMethod == "GET")
|
||||||
IOPath filePath;
|
{
|
||||||
switch (ext)
|
string urlPath = ctx.Request.Url.AbsolutePath;
|
||||||
{
|
string ext = urlPath.EndsWith(".js.map")
|
||||||
case "html":
|
? ".js.map" // extname of '*.js.map' is '.map' so it has to be set explicitely
|
||||||
filePath = Path.Concat(publicDirPath, "html", urlPath);
|
: Path.Extension(urlPath).Str;
|
||||||
ctx.Response.Headers.Set("Content-Type", "text/html");
|
IOPath filePath;
|
||||||
break;
|
switch (ext)
|
||||||
case "css":
|
{
|
||||||
filePath = Path.Concat(publicDirPath, "css", urlPath);
|
case "html":
|
||||||
ctx.Response.Headers.Set("Content-Type", "text/css");
|
filePath = Path.Concat(publicDirPath, "html", urlPath);
|
||||||
break;
|
ctx.Response.Headers.Set("Content-Type", "text/html");
|
||||||
case "js":
|
break;
|
||||||
case "js.map":
|
case "css":
|
||||||
case "jsx":
|
filePath = Path.Concat(publicDirPath, "css", urlPath);
|
||||||
case "ts":
|
ctx.Response.Headers.Set("Content-Type", "text/css");
|
||||||
case "tsx":
|
break;
|
||||||
filePath = Path.Concat(publicDirPath, "scripts", urlPath);
|
case "js":
|
||||||
ctx.Response.Headers.Set("Content-Type", "text/javascript");
|
case "js.map":
|
||||||
break;
|
case "jsx":
|
||||||
default:
|
case "ts":
|
||||||
if (urlPath == "/")
|
case "tsx":
|
||||||
{
|
filePath = Path.Concat(publicDirPath, "scripts", urlPath);
|
||||||
filePath = homepagePath;
|
ctx.Response.Headers.Set("Content-Type", "text/javascript");
|
||||||
ctx.Response.Headers.Set("Content-Type", "text/html");
|
break;
|
||||||
break;
|
default:
|
||||||
}
|
if (urlPath == "/")
|
||||||
filePath = Path.Concat(publicDirPath, urlPath);
|
{
|
||||||
ctx.Response.Headers.Set("Content-Type", "binary/octet-stream");
|
filePath = homepagePath;
|
||||||
ctx.Response.Headers.Set("Content-Disposition", "attachment filename="+filePath.LastName());
|
ctx.Response.Headers.Set("Content-Type", "text/html");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
filePath = Path.Concat(publicDirPath, urlPath);
|
||||||
if (File.Exists(filePath))
|
ctx.Response.Headers.Set("Content-Type", "binary/octet-stream");
|
||||||
status = 200;
|
ctx.Response.Headers.Set("Content-Disposition", "attachment filename="+filePath.LastName());
|
||||||
else
|
break;
|
||||||
{
|
}
|
||||||
status = 404;
|
|
||||||
if (ext == "html")
|
if (File.Exists(filePath))
|
||||||
{
|
status = 200;
|
||||||
filePath = pare404Path;
|
else
|
||||||
ctx.Response.Headers.Set("Content-Type", "text/html");
|
{
|
||||||
}
|
status = 404;
|
||||||
}
|
if (ext == "html")
|
||||||
|
{
|
||||||
if(status == 200 || ext == "html")
|
filePath = pare404Path;
|
||||||
{
|
ctx.Response.Headers.Set("Content-Type", "text/html");
|
||||||
var fileStream = File.OpenRead(filePath);
|
}
|
||||||
ctx.Response.ContentLength64 = fileStream.Length;
|
}
|
||||||
fileStream.CopyTo(ctx.Response.OutputStream);
|
|
||||||
}
|
if(status == 200 || ext == "html")
|
||||||
}
|
{
|
||||||
// reject all incorrect requests
|
var fileStream = File.OpenRead(filePath);
|
||||||
else
|
ctx.Response.ContentLength64 = fileStream.Length;
|
||||||
{
|
fileStream.CopyTo(ctx.Response.OutputStream);
|
||||||
status = 501;
|
}
|
||||||
ctx.Response.ContentLength64 = 0;
|
}
|
||||||
}
|
// reject all incorrect requests
|
||||||
ctx.Response.StatusCode = status;
|
else
|
||||||
ctx.Response.OutputStream.Flush();
|
{
|
||||||
ctx.Response.OutputStream.Close();
|
status = 400;
|
||||||
return status == 200;
|
ctx.Response.ContentLength64 = 0;
|
||||||
}
|
// ctx.Response.Headers.Set();
|
||||||
|
}
|
||||||
|
ctx.Response.StatusCode = status;
|
||||||
|
ctx.Response.OutputStream.Flush();
|
||||||
|
ctx.Response.OutputStream.Close();
|
||||||
|
return status == 200;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user