wip refactor

This commit is contained in:
GHXX 2024-01-09 06:05:15 +01:00
parent 3e307c27fa
commit a03fafebcf
4 changed files with 132 additions and 9 deletions

View File

@ -3,20 +3,20 @@
namespace SimpleHttpServer;
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class HttpEndpoint<T> : Attribute where T : IAuthorizer {
public class HttpRoute<T> : Attribute where T : IAuthorizer {
public HttpRequestType Type { get; private set; }
public HttpRequestType RequestMethod { get; private set; }
public string Location { get; private set; }
public Type Authorizer { get; private set; }
public HttpEndpoint(HttpRequestType type, string location) {
Type = type;
public HttpRoute(HttpRequestType requestMethod, string location) {
RequestMethod = requestMethod;
Location = location;
Authorizer = typeof(T);
}
}
[AttributeUsage(AttributeTargets.Method)]
public class HttpEndpoint : HttpEndpoint<DefaultAuthorizer> {
public class HttpEndpoint : HttpRoute<DefaultAuthorizer> {
public HttpEndpoint(HttpRequestType type, string location) : base(type, location) { }
}

View File

@ -6,7 +6,60 @@ namespace SimpleHttpServer;
public sealed class HttpServer {
private Thread? _listenerThread;
public int Port { get; }
private readonly CancellationTokenSource ctokSrc;
private readonly HttpListener listener;
private Task listenerTask;
private Logger logger;
public HttpServer(int port, TextWriter? logRedirect = null) {
ctokSrc = new();
Port = port;
listener = new HttpListener();
listener.Prefixes.Add($"http://localhost:{port}/");
logger = new("HttpServer", logRedirect);
}
public async Task StartAsync() {
logger.Information($"Starting on port {Port}...");
listener.Start();
listenerTask = Task.Run(GetContextLoop);
logger.Information($"Ready to handle requests!");
await Task.Yield();
}
public async Task GetContextLoop() {
while (true) {
try {
var ctx = await listener.GetContextAsync();
_ = ProcessRequestAsync(ctx);
} catch (Exception ex) {
} finally {
}
}
}
private async Task ProcessRequestAsync(HttpListenerContext ctx) {
}
private readonly Dictionary<(string path, HttpRequestType rType), Action<RequestContext>> simpleEndpoints = new();
public void RegisterRoutesFromType<T>() {
var t = typeof(T);
foreach (var (mi, attrib) in t.GetMethods()
.ToDictionary(x => x, x => x.GetCustomAttributes(typeof(HttpRoute<>)))
.Where(x => x.Value.Any()).ToDictionary(x => x.Key, x => x.Value.Single() as HttpRoute<IAuthorizer> ?? throw new InvalidCastException()))
{
simpleEndpoints.Add((attrib.Location, attrib.RequestMethod), mi.CreateDelegate<Action<RequestContext>>());
}
}
private readonly HttpListener _listener;
private readonly Dictionary<(string path, HttpRequestType rType), HttpEndpointHandler> _plainEndpoints = new();
private readonly Dictionary<(string path, HttpRequestType rType), HttpEndpointHandler> _pparamEndpoints = new();
@ -22,8 +75,8 @@ public sealed class HttpServer {
foreach (var definition in apiDefinitions) {
foreach (var endpoint in definition.GetMethods()) {
var attrib = endpoint.GetCustomAttributes()
.Where(x => x.GetType().IsAssignableTo(typeof(HttpEndpoint<>)))
.Select(x => (HttpEndpoint<IAuthorizer>) x)
.Where(x => x.GetType().IsAssignableTo(typeof(HttpRoute<>)))
.Select(x => (HttpRoute<IAuthorizer>) x)
.SingleOrDefault();
if (attrib == null) {
@ -64,6 +117,14 @@ public sealed class HttpServer {
Shutdown(-1);
}
public bool Shutdown() {
if (_listenerThread == null)
throw new InvalidOperationException("Cannot shut down HttpServer that has not been started");
}
public bool Shutdown(int timeout) {
if (_listenerThread == null) {
throw new InvalidOperationException("Cannot shutdown HttpServer that has not been started");
@ -89,7 +150,7 @@ public sealed class HttpServer {
private void RunServer() {
try {
for (; ; ) {
while (true) {
var ctx = _listener.GetContext();
ThreadPool.QueueUserWorkItem((localCtx) => {

View File

@ -0,0 +1,47 @@
using System.Diagnostics;
namespace SimpleHttpServer;
public class Logger {
private readonly string topic;
private readonly TextWriter outWriter;
internal Logger(string topic) : this(topic, Console.Out) { }
internal Logger(string topic, TextWriter? outWriter) {
this.topic = topic;
this.outWriter = outWriter ?? Console.Out;
}
private readonly object writeLock = new object();
public void Log(string message, LogOutputLevel level) {
var fgColor = level switch {
LogOutputLevel.Debug => ConsoleColor.Gray,
LogOutputLevel.Information => ConsoleColor.White,
LogOutputLevel.Warning => ConsoleColor.Yellow,
LogOutputLevel.Error => ConsoleColor.Red,
LogOutputLevel.Fatal => ConsoleColor.Magenta,
_ => throw new NotImplementedException(),
};
lock (writeLock) {
var origColor = Console.ForegroundColor;
Console.ForegroundColor = fgColor;
outWriter.WriteLine($"[{topic}] {message}");
Console.ForegroundColor = origColor;
}
}
[Conditional("DEBUG")]
public void Debug(string message) => Log(message, LogOutputLevel.Debug);
public void Information(string message) => Log(message, LogOutputLevel.Information);
public void Warning(string message) => Log(message, LogOutputLevel.Warning);
public void Error(string message) => Log(message, LogOutputLevel.Error);
public void Fatal(string message) => Log(message, LogOutputLevel.Fatal);
}
public enum LogOutputLevel {
Debug,
Information,
Warning,
Error,
Fatal
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
namespace SimpleHttpServer;
internal class RequestContext {
public RequestContext(HttpListenerContext listenerContext) {
ListenerContext = listenerContext;
}
public HttpListenerContext ListenerContext { get; }
}