More async stuff for StatusHost.

This commit is contained in:
Pieter-Jan Briers
2022-01-10 16:32:47 +01:00
parent 291a37924d
commit b6a548629a
7 changed files with 118 additions and 28 deletions

View File

@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.Primitives;
namespace Robust.Server.ServerStatus
@@ -14,30 +15,62 @@ namespace Robust.Server.ServerStatus
bool IsGetLike { get; }
IReadOnlyDictionary<string, StringValues> RequestHeaders { get; }
public T? RequestBodyJson<T>();
[Obsolete("Use async versions instead")]
T? RequestBodyJson<T>();
Task<T?> RequestBodyJsonAsync<T>();
[Obsolete("Use async versions instead")]
void Respond(
string text,
HttpStatusCode code = HttpStatusCode.OK,
string contentType = "text/plain");
[Obsolete("Use async versions instead")]
void Respond(
string text,
int code = 200,
string contentType = "text/plain");
[Obsolete("Use async versions instead")]
void Respond(
byte[] data,
HttpStatusCode code = HttpStatusCode.OK,
string contentType = "text/plain");
[Obsolete("Use async versions instead")]
void Respond(
byte[] data,
int code = 200,
string contentType = "text/plain");
Task RespondAsync(
string text,
HttpStatusCode code = HttpStatusCode.OK,
string contentType = "text/plain");
Task RespondAsync(
string text,
int code = 200,
string contentType = "text/plain");
Task RespondAsync(
byte[] data,
HttpStatusCode code = HttpStatusCode.OK,
string contentType = "text/plain");
Task RespondAsync(
byte[] data,
int code = 200,
string contentType = "text/plain");
[Obsolete("Use async versions instead")]
void RespondError(HttpStatusCode code);
Task RespondErrorAsync(HttpStatusCode code);
[Obsolete("Use async versions instead")]
void RespondJson(object jsonData, HttpStatusCode code = HttpStatusCode.OK);
Task RespondJsonAsync(object jsonData, HttpStatusCode code = HttpStatusCode.OK);
}
}

View File

@@ -7,6 +7,7 @@ namespace Robust.Server.ServerStatus
{
void Start();
[Obsolete("Use async handlers")]
void AddHandler(StatusHostHandler handler);
void AddHandler(StatusHostHandlerAsync handler);

View File

@@ -32,18 +32,18 @@ namespace Robust.Server.ServerStatus
if (!string.IsNullOrEmpty(_configurationManager.GetCVar(CVars.BuildDownloadUrl)))
{
context.Respond("This server has a build download URL.", HttpStatusCode.NotFound);
await context.RespondAsync("This server has a build download URL.", HttpStatusCode.NotFound);
return true;
}
var result = await PrepareACZ();
if (result == null)
{
context.Respond("Automatic Client Zip was not preparable.", HttpStatusCode.InternalServerError);
await context.RespondAsync("Automatic Client Zip was not preparable.", HttpStatusCode.InternalServerError);
return true;
}
context.Respond(result.Value.Data, HttpStatusCode.OK, "application/zip");
await context.RespondAsync(result.Value.Data, HttpStatusCode.OK, "application/zip");
return true;
}

View File

@@ -18,18 +18,18 @@ namespace Robust.Server.ServerStatus
AddHandler(HandleAutomaticClientZip);
}
private static bool HandleTeapot(IStatusHandlerContext context)
private static async Task<bool> HandleTeapot(IStatusHandlerContext context)
{
if (!context.IsGetLike || context.Url!.AbsolutePath != "/teapot")
{
return false;
}
context.Respond("I am a teapot.", (HttpStatusCode) 418);
await context.RespondAsync("I am a teapot.", (HttpStatusCode) 418);
return true;
}
private bool HandleStatus(IStatusHandlerContext context)
private async Task<bool> HandleStatus(IStatusHandlerContext context)
{
if (!context.IsGetLike || context.Url!.AbsolutePath != "/status")
{
@@ -46,7 +46,7 @@ namespace Robust.Server.ServerStatus
OnStatusRequest?.Invoke(jObject);
context.RespondJson(jObject);
await context.RespondJsonAsync(jObject);
return true;
}
@@ -101,7 +101,7 @@ namespace Robust.Server.ServerStatus
OnInfoRequest?.Invoke(jObject);
context.RespondJson(jObject);
await context.RespondJsonAsync(jObject);
return true;
}

View File

@@ -61,12 +61,12 @@ namespace Robust.Server.ServerStatus
// No handler returned true, assume no handlers care about this.
// 404.
apiContext.Respond("Not Found", HttpStatusCode.NotFound);
await apiContext.RespondAsync("Not Found", HttpStatusCode.NotFound);
}
catch (Exception e)
{
_httpSawmill.Error($"Exception in StatusHost: {e}");
apiContext.Respond("Internal Server Error", HttpStatusCode.InternalServerError);
await apiContext.RespondErrorAsync(HttpStatusCode.InternalServerError);
}
/*
@@ -241,6 +241,11 @@ namespace Robust.Server.ServerStatus
return JsonSerializer.Deserialize<T>(_context.Request.InputStream);
}
public async Task<T?> RequestBodyJsonAsync<T>()
{
return await JsonSerializer.DeserializeAsync<T>(_context.Request.InputStream);
}
public void Respond(string text, HttpStatusCode code = HttpStatusCode.OK, string contentType = MediaTypeNames.Text.Plain)
{
Respond(text, (int) code, contentType);
@@ -278,13 +283,47 @@ namespace Robust.Server.ServerStatus
return;
}
// Passing 'true' to this is CRITICAL.
// There's a bug in the ManagedHttpListener submodule.
// See:
// HttpListenerResponse.Managed.cs Close(byte[], bool),
// "thisRef.OutputStream.EndWrite"
// Yes, this will block, yes, you have to deal with it...
_context.Response.Close(data, true);
_context.Response.OutputStream.Write(data);
_context.Response.Close();
}
public Task RespondAsync(string text, HttpStatusCode code = HttpStatusCode.OK, string contentType = "text/plain")
{
return RespondAsync(text, (int) code, contentType);
}
public async Task RespondAsync(string text, int code = 200, string contentType = "text/plain")
{
_context.Response.StatusCode = code;
_context.Response.ContentType = contentType;
if (RequestMethod == HttpMethod.Head)
return;
using var writer = new StreamWriter(_context.Response.OutputStream, EncodingHelpers.UTF8);
await writer.WriteAsync(text);
}
public Task RespondAsync(byte[] data, HttpStatusCode code = HttpStatusCode.OK, string contentType = "text/plain")
{
return RespondAsync(data, (int) code, contentType);
}
public async Task RespondAsync(byte[] data, int code = 200, string contentType = "text/plain")
{
_context.Response.StatusCode = code;
_context.Response.ContentType = contentType;
_context.Response.ContentLength64 = data.Length;
if (RequestMethod == HttpMethod.Head)
{
_context.Response.Close();
return;
}
await _context.Response.OutputStream.WriteAsync(data);
_context.Response.Close();
}
public void RespondError(HttpStatusCode code)
@@ -292,14 +331,28 @@ namespace Robust.Server.ServerStatus
Respond(code.ToString(), code);
}
public Task RespondErrorAsync(HttpStatusCode code)
{
return RespondAsync(code.ToString(), code);
}
public void RespondJson(object jsonData, HttpStatusCode code = HttpStatusCode.OK)
{
_context.Response.ContentType = MediaTypeNames.Application.Json;
_context.Response.ContentType = "application/json";
JsonSerializer.Serialize(_context.Response.OutputStream, jsonData);
_context.Response.Close();
}
public async Task RespondJsonAsync(object jsonData, HttpStatusCode code = HttpStatusCode.OK)
{
_context.Response.ContentType = "application/json";
await JsonSerializer.SerializeAsync(_context.Response.OutputStream, jsonData);
_context.Response.Close();
}
}
}
}

View File

@@ -1,7 +1,9 @@
using System;
using System.Threading.Tasks;
namespace Robust.Server.ServerStatus
{
[Obsolete("Use async handlers")]
public delegate bool StatusHostHandler(
IStatusHandlerContext context);
public delegate Task<bool> StatusHostHandlerAsync(

View File

@@ -4,6 +4,7 @@ using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Robust.Shared;
using Robust.Shared.Asynchronous;
@@ -42,7 +43,7 @@ namespace Robust.Server.ServerStatus
_statusHost.AddHandler(ShutdownHandler);
}
private bool UpdateHandler(IStatusHandlerContext context)
private async Task<bool> UpdateHandler(IStatusHandlerContext context)
{
if (context.RequestMethod != HttpMethod.Post || context.Url!.AbsolutePath != "/update")
{
@@ -61,18 +62,18 @@ namespace Robust.Server.ServerStatus
{
// Holy shit nobody read these logs please.
_sawmill.Info(@"Failed auth: ""{0}"" vs ""{1}""", auth, _watchdogToken);
context.RespondError(HttpStatusCode.Unauthorized);
await context.RespondErrorAsync(HttpStatusCode.Unauthorized);
return true;
}
_taskManager.RunOnMainThread(() => UpdateReceived?.Invoke());
context.Respond("Success", HttpStatusCode.OK);
await context.RespondAsync("Success", HttpStatusCode.OK);
return true;
}
private bool ShutdownHandler(IStatusHandlerContext context)
private async Task<bool> ShutdownHandler(IStatusHandlerContext context)
{
if (context.RequestMethod != HttpMethod.Post || context.Url!.AbsolutePath != "/shutdown")
{
@@ -87,7 +88,7 @@ namespace Robust.Server.ServerStatus
if (!context.RequestHeaders.TryGetValue("WatchdogToken", out var auth))
{
context.Respond("Expected WatchdogToken header", HttpStatusCode.BadRequest);
await context.RespondAsync("Expected WatchdogToken header", HttpStatusCode.BadRequest);
return true;
}
@@ -96,14 +97,14 @@ namespace Robust.Server.ServerStatus
_sawmill.Warning(
"received POST /shutdown with invalid authentication token. Ignoring {0}, {1}", auth,
_watchdogToken);
context.RespondError(HttpStatusCode.Unauthorized);
await context.RespondErrorAsync(HttpStatusCode.Unauthorized);
return true;
}
ShutdownParameters? parameters = null;
try
{
parameters = context.RequestBodyJson<ShutdownParameters>();
parameters = await context.RequestBodyJsonAsync<ShutdownParameters>();
}
catch (JsonException)
{
@@ -112,14 +113,14 @@ namespace Robust.Server.ServerStatus
if (parameters == null)
{
context.RespondError(HttpStatusCode.BadRequest);
await context.RespondErrorAsync(HttpStatusCode.BadRequest);
return true;
}
_taskManager.RunOnMainThread(() => _baseServer.Shutdown(parameters.Reason));
context.Respond("Success", HttpStatusCode.OK);
await context.RespondAsync("Success", HttpStatusCode.OK);
return true;
}