From d34c41a126710fbced4ff3a7728317cf9297dc82 Mon Sep 17 00:00:00 2001 From: sam Date: Tue, 14 May 2024 14:50:01 +0200 Subject: [PATCH] add FoxchatError type --- Foxchat.Core/Database/IDatabaseContext.cs | 2 +- .../Federation/RequestSigningService.Client.cs | 13 ++++--------- .../Federation/RequestSigningService.cs | 10 +++------- Foxchat.Core/Federation/SignatureData.cs | 2 +- Foxchat.Core/FoxchatError.cs | 15 +++++++++++++++ Foxchat.Core/Models/ApiError.cs | 17 +++++++++++++++++ Foxchat.Core/UlidConverter.cs | 3 +-- 7 files changed, 42 insertions(+), 20 deletions(-) create mode 100644 Foxchat.Core/FoxchatError.cs create mode 100644 Foxchat.Core/Models/ApiError.cs diff --git a/Foxchat.Core/Database/IDatabaseContext.cs b/Foxchat.Core/Database/IDatabaseContext.cs index be8691d..9e2a707 100644 --- a/Foxchat.Core/Database/IDatabaseContext.cs +++ b/Foxchat.Core/Database/IDatabaseContext.cs @@ -29,7 +29,7 @@ public abstract class IDatabaseContext : DbContext public async Task GetInstanceAsync(CancellationToken ct = default) { var instance = await Instance.FirstOrDefaultAsync(ct) - ?? throw new Exception("GetInstanceAsync called without Instance being initialized"); // TODO: replace this with specific exception type + ?? throw new FoxchatError.DatabaseError("GetInstanceAsync called without Instance being initialized"); return instance; } diff --git a/Foxchat.Core/Federation/RequestSigningService.Client.cs b/Foxchat.Core/Federation/RequestSigningService.Client.cs index 090777d..49370e0 100644 --- a/Foxchat.Core/Federation/RequestSigningService.Client.cs +++ b/Foxchat.Core/Federation/RequestSigningService.Client.cs @@ -1,4 +1,5 @@ using System.Net.Http.Headers; +using Foxchat.Core.Models; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; @@ -9,9 +10,6 @@ public partial class RequestSigningService public const string USER_AGENT_HEADER = "User-Agent"; public const string USER_AGENT = "Foxchat.NET"; public const string DATE_HEADER = "Date"; - public const string CONTENT_LENGTH_HEADER = "Content-Length"; - public const string CONTENT_TYPE_HEADER = "Content-Type"; - public const string CONTENT_TYPE = "application/json; charset=utf-8"; public const string SERVER_HEADER = "X-Foxchat-Server"; public const string SIGNATURE_HEADER = "X-Foxchat-Signature"; @@ -28,19 +26,16 @@ public partial class RequestSigningService public async Task RequestAsync(HttpMethod method, string domain, string requestPath, string? userId = null, object? body = null) { var request = BuildHttpRequest(method, domain, requestPath, userId, body); - _logger.Debug("Content length in header: '{ContentLength}'", request.Headers.Where(c => c.Key == "Content-Length")); var resp = await _httpClient.SendAsync(request); if (!resp.IsSuccessStatusCode) { var error = await resp.Content.ReadAsStringAsync(); - _logger.Error("Received {Status}, body: {Error}", resp.StatusCode, error); - // TODO: replace this with specific exception type - throw new Exception("oh no a request error"); + throw new FoxchatError.OutgoingFederationError($"Request to {domain}/{requestPath} returned an error", DeserializeObject(error)); } var bodyString = await resp.Content.ReadAsStringAsync(); - // TODO: replace this with specific exception type - return DeserializeObject(bodyString) ?? throw new Exception("oh no invalid json"); + return DeserializeObject(bodyString) + ?? throw new FoxchatError.OutgoingFederationError($"Request to {domain}/{requestPath} returned invalid response body"); } private HttpRequestMessage BuildHttpRequest(HttpMethod method, string domain, string requestPath, string? userId = null, object? bodyData = null) diff --git a/Foxchat.Core/Federation/RequestSigningService.cs b/Foxchat.Core/Federation/RequestSigningService.cs index 88e7bbd..87f69b7 100644 --- a/Foxchat.Core/Federation/RequestSigningService.cs +++ b/Foxchat.Core/Federation/RequestSigningService.cs @@ -27,7 +27,7 @@ public partial class RequestSigningService(ILogger logger, IClock clock, IDataba formatter.SetHashAlgorithm(nameof(SHA256)); var signature = formatter.CreateSignature(hash); - _logger.Debug("Generated signature for {Host} {RequestPath}: {Signature}", data.Host, data.RequestPath, WebEncoders.Base64UrlEncode(signature)); + _logger.Debug("Generated signature for {Host} {RequestPath}", data.Host, data.RequestPath); return WebEncoders.Base64UrlEncode(signature); } @@ -41,13 +41,11 @@ public partial class RequestSigningService(ILogger logger, IClock clock, IDataba var time = ParseTime(dateHeader); if ((now + Duration.FromMinutes(1)) < time) { - // TODO: replace this with specific exception type - throw new Exception("Request was made in the future"); + throw new FoxchatError.IncomingFederationError("Request was made in the future"); } else if ((now - Duration.FromMinutes(1)) > time) { - // TODO: replace this with specific exception type - throw new Exception("Request was made too long ago"); + throw new FoxchatError.IncomingFederationError("Request was made too long ago"); } var plaintext = GeneratePlaintext(new SignatureData(time, host, requestPath, contentLength, userId)); @@ -67,8 +65,6 @@ public partial class RequestSigningService(ILogger logger, IClock clock, IDataba var contentLength = data.ContentLength != null ? data.ContentLength.ToString() : ""; var userId = data.UserId ?? ""; - Log.Information("Plaintext string: {Plaintext}", $"{time}:{data.Host}:{data.RequestPath}:{contentLength}:{userId}"); - return $"{time}:{data.Host}:{data.RequestPath}:{contentLength}:{userId}"; } diff --git a/Foxchat.Core/Federation/SignatureData.cs b/Foxchat.Core/Federation/SignatureData.cs index a8d95be..54878f2 100644 --- a/Foxchat.Core/Federation/SignatureData.cs +++ b/Foxchat.Core/Federation/SignatureData.cs @@ -8,4 +8,4 @@ public record SignatureData( string RequestPath, int? ContentLength, string? UserId -) { } +); diff --git a/Foxchat.Core/FoxchatError.cs b/Foxchat.Core/FoxchatError.cs new file mode 100644 index 0000000..61376cf --- /dev/null +++ b/Foxchat.Core/FoxchatError.cs @@ -0,0 +1,15 @@ +namespace Foxchat.Core; + +public class FoxchatError(string message) : Exception(message) +{ + public class ApiError(string message) : FoxchatError(message); + public class BadRequest(string message) : ApiError(message); + public class IncomingFederationError(string message) : FoxchatError(message); + + public class OutgoingFederationError(string message, Models.ApiError? innerError = null) : FoxchatError(message) + { + public Models.ApiError? InnerError => innerError; + } + + public class DatabaseError(string message) : FoxchatError(message); +} diff --git a/Foxchat.Core/Models/ApiError.cs b/Foxchat.Core/Models/ApiError.cs new file mode 100644 index 0000000..a9ac71d --- /dev/null +++ b/Foxchat.Core/Models/ApiError.cs @@ -0,0 +1,17 @@ +namespace Foxchat.Core.Models; + +public record ApiError(int Status, ErrorCode Code, string Message); + +public enum ErrorCode +{ + INTERNAL_SERVER_ERROR, + OBJECT_NOT_FOUND, + INVALID_SERVER, + INVALID_HEADER, + INVALID_DATE, + INVALID_SIGNATURE, + MISSING_SIGNATURE, + GUILD_NOT_FOUND, + UNAUTHORIZED, + INVALID_REST_EVENT, +} diff --git a/Foxchat.Core/UlidConverter.cs b/Foxchat.Core/UlidConverter.cs index f4af4cd..518d6e1 100644 --- a/Foxchat.Core/UlidConverter.cs +++ b/Foxchat.Core/UlidConverter.cs @@ -6,5 +6,4 @@ namespace Foxchat.Core; public class UlidConverter() : ValueConverter( convertToProviderExpression: x => x.ToGuid(), convertFromProviderExpression: x => new Ulid(x) -) -{ } +);