about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--LibMatrix/Extensions/MatrixHttpClient.Single.cs59
-rw-r--r--LibMatrix/LibMatrixNetworkException.cs33
-rw-r--r--LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs20
-rw-r--r--LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs2
-rw-r--r--LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs5
5 files changed, 92 insertions, 27 deletions
diff --git a/LibMatrix/Extensions/MatrixHttpClient.Single.cs b/LibMatrix/Extensions/MatrixHttpClient.Single.cs

index aa188dd..cd82071 100644 --- a/LibMatrix/Extensions/MatrixHttpClient.Single.cs +++ b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
@@ -5,7 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Net; using System.Net.Http.Headers; using System.Net.Http.Json; +using System.Net.Sockets; using System.Reflection; +using System.Security.Authentication; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -55,8 +57,18 @@ public class MatrixHttpClient { public Dictionary<string, string> AdditionalQueryParameters { get; set; } = new(); public Uri? BaseAddress { get; set; } - public bool RetryOnNetworkError { get; set; } = true; - public bool RetryOnMatrixError { get; set; } = true; + public static bool DefaultRetryOnNetworkError { get; set; } = true; + public static bool DefaultRetryOnMatrixError { get; set; } = true; + public bool RetryOnNetworkError { get; set; } = DefaultRetryOnNetworkError; + public bool RetryOnMatrixError { get; set; } = DefaultRetryOnMatrixError; + + public static int DefaultMinRetryIntervalMs { get; set; } = 1000; + public static int DefaultMaxRetryIntervalMs { get; set; } = 2000; + public static int DefaultMaxRetries { get; set; } = 20; + + public int MinRetryIntervalMs { get; set; } = DefaultMinRetryIntervalMs; + public int MaxRetryIntervalMs { get; set; } = DefaultMaxRetryIntervalMs; + public int MaxRetries { get; set; } = DefaultMaxRetries; private Dictionary<HttpRequestMessage, int> _retries = []; @@ -148,14 +160,23 @@ public class MatrixHttpClient { "X-Content-Security-Policy", "Referrer-Policy", "X-Robots-Tag", - "Content-Security-Policy" + "Content-Security-Policy", + "Alt-Svc", + // evil + "CF-Cache-Status", + "CF-Ray", + "x-amz-request-id", + "x-do-app-origin", + "x-do-orig-status", + "x-rgw-object-type", + "Report-To" ])); return responseMessage; } public async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken = default) { - _retries.TryAdd(request, 10); + _retries.TryAdd(request, MaxRetries); HttpResponseMessage responseMessage; try { responseMessage = await SendUnhandledAsync(request, cancellationToken); @@ -163,8 +184,27 @@ public class MatrixHttpClient { catch (HttpRequestException ex) { if (RetryOnNetworkError) { if (_retries[request]-- <= 0) throw; + // browser exceptions if (ex.InnerException?.GetType().FullName == "System.Runtime.InteropServices.JavaScript.JSException") - Console.WriteLine("Got JSException, likely a CORS error due to a reverse proxy misconfiguration and error, retrying..."); + Console.WriteLine($"Got JSException, likely a CORS error due to a reverse proxy misconfiguration and error, retrying ({_retries[request]} left)..."); + // native exceptions + else if (ex.InnerException is SocketException sockEx) + if (sockEx.SocketErrorCode == SocketError.HostNotFound) { + throw new LibMatrixNetworkException(ex) { + Error = $"Host {request.RequestUri?.Host ?? "(null)"} not found", + ErrorCode = LibMatrixNetworkException.ErrorCodes.RLM_NET_UNKNOWN_HOST + }; + } + else { } // empty + else if (ex.InnerException is AuthenticationException authEx) + if (authEx.Message.Contains("The remote certificate is invalid")) { + throw new LibMatrixNetworkException(ex) { + Error = ex.Message, + ErrorCode = LibMatrixNetworkException.ErrorCodes.RLM_NET_INVALID_REMOTE_CERTIFICATE + }; + } + else { } // empty + else Console.WriteLine(new { ex.HttpRequestError, @@ -175,10 +215,11 @@ public class MatrixHttpClient { InnerExceptionType = ex.InnerException?.GetType().FullName }.ToJson()); - await Task.Delay(Random.Shared.Next(1000, 2000), cancellationToken); + await Task.Delay(Random.Shared.Next(MinRetryIntervalMs, MaxRetryIntervalMs), cancellationToken); request.ResetSendStatus(); return await SendAsync(request, cancellationToken); } + throw; } @@ -221,7 +262,7 @@ public class MatrixHttpClient { if (ex.ErrorCode == MatrixException.ErrorCodes.M_LIMIT_EXCEEDED) { // if (ex.RetryAfterMs is null) throw ex!; //we have a ratelimit error - await Task.Delay(ex.RetryAfterMs ?? responseMessage.Headers.RetryAfter?.Delta?.Milliseconds ?? 500, cancellationToken); + await Task.Delay(ex.RetryAfterMs ?? responseMessage.Headers.RetryAfter?.Delta?.Milliseconds ?? MinRetryIntervalMs, cancellationToken); request.ResetSendStatus(); return await SendAsync(request, cancellationToken); } @@ -233,8 +274,8 @@ public class MatrixHttpClient { // spread out retries if (RetryOnNetworkError) { if (_retries[request]-- <= 0) throw new InvalidDataException("Encountered invalid data:\n" + content); - Console.WriteLine("Got 502 Bad Gateway, retrying..."); - await Task.Delay(Random.Shared.Next(1000, 2000), cancellationToken); + Console.WriteLine($"Got 502 Bad Gateway, retrying ({_retries[request]} left)..."); + await Task.Delay(Random.Shared.Next(MinRetryIntervalMs, MaxRetryIntervalMs), cancellationToken); request.ResetSendStatus(); return await SendAsync(request, cancellationToken); } diff --git a/LibMatrix/LibMatrixNetworkException.cs b/LibMatrix/LibMatrixNetworkException.cs new file mode 100644
index 0000000..7be0f4e --- /dev/null +++ b/LibMatrix/LibMatrixNetworkException.cs
@@ -0,0 +1,33 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using ArcaneLibs.Extensions; +// ReSharper disable MemberCanBePrivate.Global + +namespace LibMatrix; + +public class LibMatrixNetworkException : Exception { + public LibMatrixNetworkException() : base() { } + public LibMatrixNetworkException(Exception httpRequestException) : base("A network error occurred", httpRequestException) { } + + [JsonPropertyName("errcode")] + public required string ErrorCode { get; set; } + + [JsonPropertyName("error")] + public required string Error { get; set; } + + public object GetAsObject() => new { errcode = ErrorCode, error = Error }; + public string GetAsJson() => GetAsObject().ToJson(ignoreNull: true); + + public override string Message => + $"{ErrorCode}: {ErrorCode switch { + ErrorCodes.RLM_NET_UNKNOWN_HOST => "The specified host could not be found.", + ErrorCodes.RLM_NET_INVALID_REMOTE_CERTIFICATE => "The remote server's TLS certificate is invalid or could not be verified.", + _ => $"Unknown error: {GetAsObject().ToJson(ignoreNull: true)}" + }}\nError: {Error}"; + + [SuppressMessage("ReSharper", "InconsistentNaming", Justification = "Follows spec naming")] + public static class ErrorCodes { + public const string RLM_NET_UNKNOWN_HOST = "RLM_NET_UNKNOWN_HOST"; + public const string RLM_NET_INVALID_REMOTE_CERTIFICATE = "RLM_NET_INVALID_REMOTE_CERTIFICATE"; + } +} \ No newline at end of file diff --git a/LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs b/LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs
index 4c78347..c5e9d9c 100644 --- a/LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs +++ b/LibMatrix/Services/WellKnownResolver/WellKnownResolverService.cs
@@ -34,17 +34,13 @@ public class WellKnownResolverService { WellKnownResolverConfiguration? config = null) { WellKnownRecords records = new(); _logger.LogDebug($"Resolving well-knowns for {homeserver}"); - if (includeClient && await _clientWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration) is { } clientResult) { - records.ClientWellKnown = clientResult; - } - - if (includeServer && await _serverWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration) is { } serverResult) { - records.ServerWellKnown = serverResult; - } - - if (includeSupport && await _supportWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration) is { } supportResult) { - records.SupportWellKnown = supportResult; - } + var clientTask = _clientWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration); + var serverTask = _serverWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration); + var supportTask = _supportWellKnownResolver.TryResolveWellKnown(homeserver, config ?? _configuration); + + if (includeClient && await clientTask is { } clientResult) records.ClientWellKnown = clientResult; + if (includeServer && await serverTask is { } serverResult) records.ServerWellKnown = serverResult; + if (includeSupport && await supportTask is { } supportResult) records.SupportWellKnown = supportResult; return records; } @@ -75,8 +71,10 @@ public class WellKnownResolverService { public struct WellKnownResolutionWarning { public WellKnownResolutionWarningType Type { get; set; } public string Message { get; set; } + [JsonIgnore] public Exception? Exception { get; set; } + public string? ExceptionMessage => Exception?.Message; [JsonConverter(typeof(JsonStringEnumConverter))] diff --git a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs
index f8de38d..f52b217 100644 --- a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs +++ b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ClientWellKnownResolver.cs
@@ -14,8 +14,6 @@ public class ClientWellKnownResolver(ILogger<ClientWellKnownResolver> logger, We StoreNulls = false }; - private static readonly MatrixHttpClient HttpClient = new(); - public Task<WellKnownResolverService.WellKnownResolutionResult<ClientWellKnown>> TryResolveWellKnown(string homeserver, WellKnownResolverConfiguration? config = null) { config ??= configuration; return ClientWellKnownCache.TryGetOrAdd(homeserver, async () => { diff --git a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs
index a99185c..a48d846 100644 --- a/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs +++ b/LibMatrix/Services/WellKnownResolver/WellKnownResolvers/ServerWellKnownResolver.cs
@@ -1,6 +1,5 @@ using System.Text.Json.Serialization; using ArcaneLibs.Collections; -using LibMatrix.Extensions; using Microsoft.Extensions.Logging; using WellKnownType = LibMatrix.Services.WellKnownResolver.WellKnownResolvers.ServerWellKnown; using ResultType = @@ -14,8 +13,6 @@ public class ServerWellKnownResolver(ILogger<ServerWellKnownResolver> logger, We StoreNulls = false }; - private static readonly MatrixHttpClient HttpClient = new(); - public Task<WellKnownResolverService.WellKnownResolutionResult<ServerWellKnown>> TryResolveWellKnown(string homeserver, WellKnownResolverConfiguration? config = null) { config ??= configuration; return ClientWellKnownCache.TryGetOrAdd(homeserver, async () => { @@ -25,13 +22,11 @@ public class ServerWellKnownResolver(ILogger<ServerWellKnownResolver> logger, We await TryGetWellKnownFromUrl($"https://{homeserver}/.well-known/matrix/server", WellKnownResolverService.WellKnownSource.Https); if (result.Content != null) return result; - return result; }); } } - public class ServerWellKnown { [JsonPropertyName("m.server")] public string Homeserver { get; set; }