about summary refs log tree commit diff
path: root/LibMatrix/Extensions/MatrixHttpClient.Single.cs
diff options
context:
space:
mode:
Diffstat (limited to 'LibMatrix/Extensions/MatrixHttpClient.Single.cs')
-rw-r--r--LibMatrix/Extensions/MatrixHttpClient.Single.cs106
1 files changed, 88 insertions, 18 deletions
diff --git a/LibMatrix/Extensions/MatrixHttpClient.Single.cs b/LibMatrix/Extensions/MatrixHttpClient.Single.cs

index 39eb7e5..671566f 100644 --- a/LibMatrix/Extensions/MatrixHttpClient.Single.cs +++ b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
@@ -1,20 +1,21 @@ #define SINGLE_HTTPCLIENT // Use a single HttpClient instance for all MatrixHttpClient instances -// #define SYNC_HTTPCLIENT // Only allow one request as a time, for debugging +// #define SYNC_HTTPCLIENT // Only allow one request as a time, for debugging using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Net; using System.Net.Http.Headers; using System.Reflection; -using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using ArcaneLibs; using ArcaneLibs.Extensions; +using LibMatrix.Homeservers.ImplementationDetails.Synapse.Models.Requests; namespace LibMatrix.Extensions; #if SINGLE_HTTPCLIENT -// TODO: Add URI wrapper for +// TODO: Add URI wrapper for public class MatrixHttpClient { private static readonly HttpClient Client; @@ -50,6 +51,7 @@ public class MatrixHttpClient { internal SemaphoreSlim _rateLimitSemaphore { get; } = new(1, 1); #endif + public static bool LogRequests = true; public Dictionary<string, string> AdditionalQueryParameters { get; set; } = new(); public Uri? BaseAddress { get; set; } @@ -59,7 +61,7 @@ public class MatrixHttpClient { typeof(HttpRequestHeaders).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [], null)?.Invoke([]) as HttpRequestHeaders ?? throw new InvalidOperationException("Failed to create HttpRequestHeaders"); - private JsonSerializerOptions GetJsonSerializerOptions(JsonSerializerOptions? options = null) { + private static JsonSerializerOptions GetJsonSerializerOptions(JsonSerializerOptions? options = null) { options ??= new JsonSerializerOptions(); options.Converters.Add(new JsonFloatStringConverter()); options.Converters.Add(new JsonDoubleStringConverter()); @@ -68,27 +70,54 @@ public class MatrixHttpClient { return options; } - public async Task<HttpResponseMessage> SendUnhandledAsync(HttpRequestMessage request, CancellationToken cancellationToken) { + public async Task<HttpResponseMessage> SendUnhandledAsync(HttpRequestMessage request, CancellationToken cancellationToken, int attempt = 0) { + if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null"); + // if (!request.RequestUri.IsAbsoluteUri) + request.RequestUri = request.RequestUri.EnsureAbsolute(BaseAddress!); + var swWait = Stopwatch.StartNew(); #if SYNC_HTTPCLIENT await _rateLimitSemaphore.WaitAsync(cancellationToken); #endif - Console.WriteLine($"Sending {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)})"); - if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null"); - if (!request.RequestUri.IsAbsoluteUri) request.RequestUri = new Uri(BaseAddress, request.RequestUri); + if (!request.RequestUri.IsAbsoluteUri) + request.RequestUri = new Uri(BaseAddress ?? throw new InvalidOperationException("Relative URI passed, but no BaseAddress is specified!"), request.RequestUri); + swWait.Stop(); + var swExec = Stopwatch.StartNew(); + foreach (var (key, value) in AdditionalQueryParameters) request.RequestUri = request.RequestUri.AddQuery(key, value); - foreach (var (key, value) in DefaultRequestHeaders) request.Headers.Add(key, value); + foreach (var (key, value) in DefaultRequestHeaders) { + if (request.Headers.Contains(key)) continue; + request.Headers.Add(key, value); + } request.Options.Set(new HttpRequestOptionsKey<bool>("WebAssemblyEnableStreamingResponse"), true); + if (LogRequests) + Console.WriteLine("Sending " + request.Summarise(includeHeaders: true, includeQuery: true, includeContentIfText: false, hideHeaders: ["Accept"])); + HttpResponseMessage? responseMessage; try { responseMessage = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); } catch (Exception e) { - Console.WriteLine( - $"Failed to send request {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}):\n{e}"); + // if (attempt >= 5) { + // Console.WriteLine( + // $"Failed to send request {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}):\n{e}"); + // throw; + // } + // + // if (e is TaskCanceledException or TimeoutException or HttpRequestException) { + // if (request.Method == HttpMethod.Get && !cancellationToken.IsCancellationRequested) { + // await Task.Delay(Random.Shared.Next(100, 1000), cancellationToken); + // request.ResetSendStatus(); + // return await SendUnhandledAsync(request, cancellationToken, attempt + 1); + // } + // } + // else if (!e.ToString().StartsWith("TypeError: NetworkError")) + // Console.WriteLine( + // $"Failed to send request {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}):\n{e}"); + throw; } #if SYNC_HTTPCLIENT @@ -97,8 +126,26 @@ public class MatrixHttpClient { } #endif - Console.WriteLine( - $"Sending {request.Method} {request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}) -> {(int)responseMessage.StatusCode} {responseMessage.StatusCode} ({Util.BytesToString(responseMessage.Content.Headers.ContentLength ?? 0)})"); + // Console.WriteLine($"Sending {request.Method} {request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}) -> {(int)responseMessage.StatusCode} {responseMessage.StatusCode} ({Util.BytesToString(responseMessage.GetContentLength())}, WAIT={swWait.ElapsedMilliseconds}ms, EXEC={swExec.ElapsedMilliseconds}ms)"); + if (LogRequests) + Console.WriteLine("Received " + responseMessage.Summarise(includeHeaders: true, includeContentIfText: false, hideHeaders: [ + "Server", + "Date", + "Transfer-Encoding", + "Connection", + "Vary", + "Content-Length", + "Access-Control-Allow-Origin", + "Access-Control-Allow-Methods", + "Access-Control-Allow-Headers", + "Access-Control-Expose-Headers", + "Cache-Control", + "Cross-Origin-Resource-Policy", + "X-Content-Security-Policy", + "Referrer-Policy", + "X-Robots-Tag", + "Content-Security-Policy" + ])); return responseMessage; } @@ -107,6 +154,12 @@ public class MatrixHttpClient { var responseMessage = await SendUnhandledAsync(request, cancellationToken); if (responseMessage.IsSuccessStatusCode) return responseMessage; + //retry on gateway timeout + // if (responseMessage.StatusCode == HttpStatusCode.GatewayTimeout) { + // request.ResetSendStatus(); + // return await SendAsync(request, cancellationToken); + // } + //error handling var content = await responseMessage.Content.ReadAsStringAsync(cancellationToken); if (content.Length == 0) @@ -114,10 +167,15 @@ public class MatrixHttpClient { ErrorCode = "M_UNKNOWN", Error = "Unknown error, server returned no content" }; - if (!content.StartsWith('{')) throw new InvalidDataException("Encountered invalid data:\n" + content); + + // if (!content.StartsWith('{')) throw new InvalidDataException("Encountered invalid data:\n" + content); + if (!content.TrimStart().StartsWith('{')) { + responseMessage.EnsureSuccessStatusCode(); + throw new InvalidDataException("Encountered invalid data:\n" + content); + } //we have a matrix error - MatrixException? ex = null; + MatrixException? ex; try { ex = JsonSerializer.Deserialize<MatrixException>(content); } @@ -131,7 +189,7 @@ public class MatrixHttpClient { Debug.Assert(ex != null, nameof(ex) + " != null"); ex.RawContent = content; // Console.WriteLine($"Failed to send request: {ex}"); - if (ex?.RetryAfterMs is null) throw ex!; + if (ex.RetryAfterMs is null) throw ex!; //we have a ratelimit error await Task.Delay(ex.RetryAfterMs.Value, cancellationToken); request.ResetSendStatus(); @@ -170,7 +228,7 @@ public class MatrixHttpClient { } // GetStreamAsync - public new async Task<Stream> GetStreamAsync(string requestUri, CancellationToken cancellationToken = default) { + public async Task<Stream> GetStreamAsync(string requestUri, CancellationToken cancellationToken = default) { var request = new HttpRequestMessage(HttpMethod.Get, requestUri); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await SendAsync(request, cancellationToken); @@ -209,7 +267,7 @@ public class MatrixHttpClient { await foreach (var resp in result) yield return resp; } - public async Task<bool> CheckSuccessStatus(string url) { + public static async Task<bool> CheckSuccessStatus(string url) { //cors causes failure, try to catch try { var resp = await Client.GetAsync(url); @@ -227,5 +285,17 @@ public class MatrixHttpClient { }; return await SendAsync(request, cancellationToken); } + + public async Task<HttpResponseMessage> DeleteAsync(string url) { + var request = new HttpRequestMessage(HttpMethod.Delete, url); + return await SendAsync(request); + } + + public async Task<HttpResponseMessage> DeleteAsJsonAsync<T>(string url, T payload) { + var request = new HttpRequestMessage(HttpMethod.Delete, url) { + Content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json") + }; + return await SendAsync(request); + } } #endif \ No newline at end of file