using System.Net.Http; using System.Net.Http.Headers; using System.Reflection; namespace Zcbot.WindowsNode; internal sealed class NodeHttpClient : IDisposable { private readonly HttpClient client; internal NodeHttpClient(NodeConfig config, HttpMessageHandler? handler = null) { client = handler is null ? new HttpClient() : new HttpClient(handler, disposeHandler: true); client.BaseAddress = config.ServerUrl; client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", config.NodeToken); client.DefaultRequestHeaders.Add("X-Node-Id", config.NodeId.ToString()); var version = Assembly.GetExecutingAssembly().GetName().Version?.ToString(3) ?? "0.1.0"; client.DefaultRequestHeaders.UserAgent.ParseAdd($"zcbot-windows-node/{version}"); } internal HttpRequestMessage CreateRequest( HttpMethod method, string path, RecoverableJob? job = null, HttpContent? content = null) { var request = new HttpRequestMessage(method, path) { Content = content }; if (job is not null) { request.Headers.Add("X-Lease-Id", job.LeaseId.ToString()); request.Headers.Add("X-Request-Digest", job.RequestDigest); } return request; } internal Task SendAsync( HttpRequestMessage request, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead, CancellationToken cancellationToken = default) => client.SendAsync(request, completionOption, cancellationToken); internal async Task SendWithRetryAsync( Func requestFactory, HttpCompletionOption completionOption, CancellationToken cancellationToken) { for (var attempt = 0; ; attempt++) { using var request = requestFactory(); try { var response = await SendAsync(request, completionOption, cancellationToken); var delay = TransferRetryPolicy.RetryDelay( request.Method, attempt, response.StatusCode); if (delay is null) { return response; } response.Dispose(); await Task.Delay(delay.Value, cancellationToken); } catch (HttpRequestException exception) { var delay = TransferRetryPolicy.RetryDelay( request.Method, attempt, exception: exception); if (delay is null) { throw; } await Task.Delay(delay.Value, cancellationToken); } } } public void Dispose() => client.Dispose(); }