using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Net.Http.Headers; using System.Reflection; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using ArcaneLibs.Extensions; using Microsoft.AspNetCore.Components.WebAssembly.Http; namespace SystemdCtl.Client.Abstractions; public class StreamingHttpClient : HttpClient { public Dictionary AdditionalQueryParameters { get; set; } = new(); internal string? AssertedUserId { get; set; } private JsonSerializerOptions GetJsonSerializerOptions(JsonSerializerOptions? options = null) { options ??= new JsonSerializerOptions(); options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; return options; } public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null"); if (!request.RequestUri.IsAbsoluteUri) request.RequestUri = new Uri(BaseAddress, request.RequestUri); // if (AssertedUserId is not null) request.RequestUri = request.RequestUri.AddQuery("user_id", AssertedUserId); foreach (var (key, value) in AdditionalQueryParameters) { request.RequestUri = request.RequestUri.AddQuery(key, value); } Console.WriteLine($"Sending request to {request.RequestUri}"); try { var webAssemblyEnableStreamingResponseKey = new HttpRequestOptionsKey("WebAssemblyEnableStreamingResponse"); request.Options.Set(webAssemblyEnableStreamingResponseKey, true); } catch (Exception e) { Console.WriteLine("Failed to set browser response streaming:"); Console.WriteLine(e); } HttpResponseMessage responseMessage; // try { responseMessage = await base.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken); // } // catch (Exception e) { // if (requestSettings is { Retries: 0 }) throw; // typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance) // ?.SetValue(request, 0); // await Task.Delay(requestSettings?.RetryDelay ?? 2500, cancellationToken); // if(requestSettings is not null) requestSettings.Retries--; // return await SendAsync(request, cancellationToken); // throw; // } if (responseMessage.IsSuccessStatusCode) return responseMessage; //error handling // var content = await responseMessage.Content.ReadAsStringAsync(cancellationToken); // typeof(HttpRequestMessage).GetField("_sendStatus", BindingFlags.NonPublic | BindingFlags.Instance) // ?.SetValue(request, 0); // return await SendAsync(request, cancellationToken); return responseMessage; } // GetAsync public Task GetAsync([StringSyntax("Uri")] string? requestUri, CancellationToken? cancellationToken = null) { return SendAsync(new HttpRequestMessage(HttpMethod.Get, requestUri), cancellationToken ?? CancellationToken.None); } // GetFromJsonAsync public async Task GetFromJsonAsync(string requestUri, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { options = GetJsonSerializerOptions(options); // Console.WriteLine($"GetFromJsonAsync called for {requestUri} with json options {options?.ToJson(ignoreNull:true)} and cancellation token {cancellationToken}"); var request = new HttpRequestMessage(HttpMethod.Get, requestUri); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); var response = await SendAsync(request, cancellationToken); response.EnsureSuccessStatusCode(); await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken); return await JsonSerializer.DeserializeAsync(responseStream, options, cancellationToken: cancellationToken) ?? throw new InvalidOperationException("Failed to deserialize response"); } // GetStreamAsync public new async Task 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); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStreamAsync(cancellationToken); } public async Task PutAsJsonAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, T value, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) where T : notnull { options = GetJsonSerializerOptions(options); var request = new HttpRequestMessage(HttpMethod.Put, requestUri); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); Console.WriteLine($"Sending PUT {requestUri}"); request.Content = new StringContent(JsonSerializer.Serialize(value, value.GetType(), options), Encoding.UTF8, "application/json"); return await SendAsync(request, cancellationToken); } public async Task PostAsJsonAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, T value, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) where T : notnull { options ??= new(); options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; var request = new HttpRequestMessage(HttpMethod.Post, requestUri); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Content = new StringContent(JsonSerializer.Serialize(value, value.GetType(), options), Encoding.UTF8, "application/json"); return await SendAsync(request, cancellationToken); } public async IAsyncEnumerable GetAsyncEnumerableFromJsonAsync([StringSyntax(StringSyntaxAttribute.Uri)] string? requestUri, JsonSerializerOptions? options = null) { options = GetJsonSerializerOptions(options); var res = await GetAsync(requestUri); var result = JsonSerializer.DeserializeAsyncEnumerable(await res.Content.ReadAsStreamAsync(), options); await foreach (var resp in result) { yield return resp; } } public IAsyncEnumerable GetFromJsonAsAsyncEnumerable( [StringSyntax("Uri")] string? requestUri, CancellationToken cancellationToken = default(CancellationToken)) => GetAsyncEnumerableFromJsonAsync(requestUri); }