wip refactor
This commit is contained in:
parent
3e307c27fa
commit
a03fafebcf
|
|
@ -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) { }
|
||||
}
|
||||
|
|
@ -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) => {
|
||||
|
|
|
|||
47
SimpleHttpServer/Logger.cs
Normal file
47
SimpleHttpServer/Logger.cs
Normal 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
|
||||
}
|
||||
15
SimpleHttpServer/RequestContext.cs
Normal file
15
SimpleHttpServer/RequestContext.cs
Normal 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; }
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user