add ParsedParameters to RequestContext, cleanup error handling
This commit is contained in:
parent
8cdff9268a
commit
6cc849bf01
|
|
@ -213,11 +213,11 @@ public sealed class HttpServer {
|
||||||
foreach (var queryKV in queryStringArgs) {
|
foreach (var queryKV in queryStringArgs) {
|
||||||
var queryKVSplitted = queryKV.Split('=');
|
var queryKVSplitted = queryKV.Split('=');
|
||||||
if (queryKVSplitted.Length != 2) {
|
if (queryKVSplitted.Length != 2) {
|
||||||
await rc.SetStatusCodeAndDisposeAsync(HttpStatusCode.BadRequest, "Malformed request URL parameters");
|
await HandleDefaultErrorPageAsync(rc, HttpStatusCode.BadRequest, "Malformed request URL parameters");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!parsedQParams.TryAdd(WebUtility.UrlDecode(queryKVSplitted[0]), WebUtility.UrlDecode(queryKVSplitted[1]))) {
|
if (!parsedQParams.TryAdd(WebUtility.UrlDecode(queryKVSplitted[0]), WebUtility.UrlDecode(queryKVSplitted[1]))) {
|
||||||
await rc.SetStatusCodeAndDisposeAsync(HttpStatusCode.BadRequest, "Duplicate request URL parameters");
|
await HandleDefaultErrorPageAsync(rc, HttpStatusCode.BadRequest, "Duplicate request URL parameters");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -230,20 +230,21 @@ public sealed class HttpServer {
|
||||||
if (stringToTypeParameterConverters[qparamInfo.type].TryConvertFromString(qparamValue, out object objRes)) {
|
if (stringToTypeParameterConverters[qparamInfo.type].TryConvertFromString(qparamValue, out object objRes)) {
|
||||||
convertedQParamValues[i] = objRes;
|
convertedQParamValues[i] = objRes;
|
||||||
} else {
|
} else {
|
||||||
await rc.SetStatusCodeAndDisposeAsync(HttpStatusCode.BadRequest);
|
await HandleDefaultErrorPageAsync(rc, HttpStatusCode.BadRequest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (qparamInfo.isOptional) {
|
if (qparamInfo.isOptional) {
|
||||||
convertedQParamValues[i] = null!;
|
convertedQParamValues[i] = null!;
|
||||||
} else {
|
} else {
|
||||||
await rc.SetStatusCodeAndDisposeAsync(HttpStatusCode.BadRequest, $"Missing required query parameter {qparamName}");
|
await HandleDefaultErrorPageAsync(rc, HttpStatusCode.BadRequest, $"Missing required query parameter {qparamName}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
convertedQParamValues[0] = rc;
|
convertedQParamValues[0] = rc;
|
||||||
|
rc.ParsedParameters = parsedQParams.AsReadOnly();
|
||||||
|
|
||||||
await (Task) (mi.Invoke(null, convertedQParamValues) ?? throw new NullReferenceException("Website func returned null unexpectedly"));
|
await (Task) (mi.Invoke(null, convertedQParamValues) ?? throw new NullReferenceException("Website func returned null unexpectedly"));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -256,7 +257,7 @@ public sealed class HttpServer {
|
||||||
|
|
||||||
if (Path.GetRelativePath(v, staticResponsePath).Contains("..")) {
|
if (Path.GetRelativePath(v, staticResponsePath).Contains("..")) {
|
||||||
requestLogger.Warning($"Blocked GET request to {reqPath} as somehow the target file does not lie inside the static serve folder? Are you using symlinks?");
|
requestLogger.Warning($"Blocked GET request to {reqPath} as somehow the target file does not lie inside the static serve folder? Are you using symlinks?");
|
||||||
await rc.SetStatusCodeAndDisposeAsync(HttpStatusCode.NotFound);
|
await HandleDefaultErrorPageAsync(rc, HttpStatusCode.NotFound);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -265,7 +266,7 @@ public sealed class HttpServer {
|
||||||
using var f = File.OpenRead(staticResponsePath);
|
using var f = File.OpenRead(staticResponsePath);
|
||||||
await f.CopyToAsync(rc.ListenerContext.Response.OutputStream);
|
await f.CopyToAsync(rc.ListenerContext.Response.OutputStream);
|
||||||
} else {
|
} else {
|
||||||
await rc.SetStatusCodeAndDisposeAsync(HttpStatusCode.NotFound);
|
await HandleDefaultErrorPageAsync(rc, HttpStatusCode.NotFound);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -281,19 +282,27 @@ public sealed class HttpServer {
|
||||||
} finally {
|
} finally {
|
||||||
try { await rc.RespWriter.FlushAsync(); } catch (ObjectDisposedException) { }
|
try { await rc.RespWriter.FlushAsync(); } catch (ObjectDisposedException) { }
|
||||||
rc.ListenerContext.Response.Close();
|
rc.ListenerContext.Response.Close();
|
||||||
}
|
|
||||||
|
|
||||||
LogRequest();
|
LogRequest();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task HandleDefaultErrorPageAsync(RequestContext ctx, HttpStatusCode errorCode, string? statusDescription = null) => await HandleDefaultErrorPageAsync(ctx, (int) errorCode, statusDescription);
|
||||||
|
|
||||||
private static async Task HandleDefaultErrorPageAsync(RequestContext ctx, int errorCode) {
|
private static async Task HandleDefaultErrorPageAsync(RequestContext ctx, int errorCode, string? statusDescription = null) {
|
||||||
ctx.SetStatusCode(errorCode);
|
ctx.SetStatusCode(errorCode);
|
||||||
|
string desc = statusDescription != null ? $"\r\n{statusDescription}" : "";
|
||||||
await ctx.WriteLineToRespAsync($"""
|
await ctx.WriteLineToRespAsync($"""
|
||||||
<body>
|
<body>
|
||||||
<h1>Oh no, an error occurred!</h1>
|
<h1>Oh no, an error occurred!</h1>
|
||||||
<p>Code: {errorCode}</p>
|
<p>Code: {errorCode}</p>{desc}
|
||||||
</body>
|
</body>
|
||||||
""");
|
""");
|
||||||
|
try {
|
||||||
|
if (statusDescription == null) {
|
||||||
|
await ctx.SetStatusCodeAndDisposeAsync(errorCode);
|
||||||
|
} else {
|
||||||
|
await ctx.SetStatusCodeAndDisposeAsync(errorCode, statusDescription);
|
||||||
|
}
|
||||||
|
} catch (ObjectDisposedException) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
using System.Net;
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace SimpleHttpServer;
|
namespace SimpleHttpServer;
|
||||||
public class RequestContext : IDisposable {
|
public class RequestContext : IDisposable {
|
||||||
|
|
||||||
public HttpListenerContext ListenerContext { get; }
|
public HttpListenerContext ListenerContext { get; }
|
||||||
|
public ReadOnlyDictionary<string, string> ParsedParameters { get; internal set; }
|
||||||
|
|
||||||
private StreamReader? reqReader;
|
private StreamReader? reqReader;
|
||||||
public StreamReader ReqReader => reqReader ??= new(ListenerContext.Request.InputStream);
|
public StreamReader ReqReader => reqReader ??= new(ListenerContext.Request.InputStream);
|
||||||
|
|
@ -29,6 +31,7 @@ public class RequestContext : IDisposable {
|
||||||
using (this) {
|
using (this) {
|
||||||
SetStatusCode(status);
|
SetStatusCode(status);
|
||||||
await WriteToRespAsync("\n\n");
|
await WriteToRespAsync("\n\n");
|
||||||
|
await RespWriter.FlushAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,6 +39,7 @@ public class RequestContext : IDisposable {
|
||||||
using (this) {
|
using (this) {
|
||||||
SetStatusCode((int) status);
|
SetStatusCode((int) status);
|
||||||
await WriteToRespAsync("\n\n");
|
await WriteToRespAsync("\n\n");
|
||||||
|
await RespWriter.FlushAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,11 +49,17 @@ public class RequestContext : IDisposable {
|
||||||
ListenerContext.Response.StatusCode = status;
|
ListenerContext.Response.StatusCode = status;
|
||||||
ListenerContext.Response.StatusDescription = description;
|
ListenerContext.Response.StatusDescription = description;
|
||||||
await WriteToRespAsync("\n\n");
|
await WriteToRespAsync("\n\n");
|
||||||
|
await RespWriter.FlushAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public async Task SetStatusCodeAndDisposeAsync(HttpStatusCode status, string description) => await SetStatusCodeAndDisposeAsync((int) status, description);
|
public async Task SetStatusCodeAndDisposeAsync(HttpStatusCode status, string description) => await SetStatusCodeAndDisposeAsync((int) status, description);
|
||||||
|
|
||||||
|
|
||||||
|
public async Task WriteRedirect302AndDisposeAsync(string url) {
|
||||||
|
ListenerContext.Response.AddHeader("Location", url);
|
||||||
|
await SetStatusCodeAndDisposeAsync(HttpStatusCode.Redirect);
|
||||||
|
}
|
||||||
|
|
||||||
void IDisposable.Dispose() {
|
void IDisposable.Dispose() {
|
||||||
reqReader?.Dispose();
|
reqReader?.Dispose();
|
||||||
respWriter?.Dispose();
|
respWriter?.Dispose();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user