Files
ai-stylegallery/PhotoboothUploader/Services/PhotoboothConnectClient.cs

123 lines
4.1 KiB
C#

using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using PhotoboothUploader.Models;
namespace PhotoboothUploader.Services;
public sealed class PhotoboothConnectClient
{
private const int MaxRetries = 2;
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(10);
private readonly HttpClient _httpClient;
private readonly JsonSerializerOptions _jsonOptions = new()
{
PropertyNameCaseInsensitive = true,
};
public PhotoboothConnectClient(string baseUrl, string userAgent)
{
_httpClient = new HttpClient
{
BaseAddress = new Uri(baseUrl),
Timeout = DefaultTimeout,
};
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
}
public async Task<PhotoboothConnectResponse> RedeemAsync(string code, CancellationToken cancellationToken = default)
{
var request = new { code };
for (var attempt = 0; attempt <= MaxRetries; attempt++)
{
try
{
using var response = await _httpClient.PostAsJsonAsync("/api/v1/photobooth/connect", request, cancellationToken);
var payload = await ReadPayloadAsync(response, cancellationToken);
if (response.IsSuccessStatusCode)
{
return payload ?? Fail(response.ReasonPhrase ?? "Verbindung fehlgeschlagen.");
}
if (response.StatusCode is HttpStatusCode.UnprocessableEntity or HttpStatusCode.Conflict or HttpStatusCode.Unauthorized)
{
return payload ?? Fail(response.ReasonPhrase ?? "Verbindung fehlgeschlagen.");
}
if (attempt < MaxRetries && IsTransientStatus(response.StatusCode))
{
await Task.Delay(GetRetryDelay(attempt), cancellationToken);
continue;
}
return payload ?? Fail(response.ReasonPhrase ?? "Verbindung fehlgeschlagen.");
}
catch (TaskCanceledException) when (!cancellationToken.IsCancellationRequested)
{
if (attempt < MaxRetries)
{
await Task.Delay(GetRetryDelay(attempt), cancellationToken);
continue;
}
return Fail("Zeitüberschreitung bei der Verbindung.");
}
catch (HttpRequestException)
{
if (attempt < MaxRetries)
{
await Task.Delay(GetRetryDelay(attempt), cancellationToken);
continue;
}
return Fail("Netzwerkfehler. Bitte Verbindung prüfen.");
}
catch (JsonException)
{
return Fail("Serverantwort konnte nicht gelesen werden.");
}
}
return Fail("Verbindung fehlgeschlagen.");
}
private async Task<PhotoboothConnectResponse?> ReadPayloadAsync(HttpResponseMessage response, CancellationToken cancellationToken)
{
if (response.Content.Headers.ContentLength == 0)
{
return null;
}
return await response.Content.ReadFromJsonAsync<PhotoboothConnectResponse>(_jsonOptions, cancellationToken);
}
private static bool IsTransientStatus(HttpStatusCode statusCode)
{
return statusCode is HttpStatusCode.RequestTimeout or HttpStatusCode.TooManyRequests
or HttpStatusCode.BadGateway or HttpStatusCode.ServiceUnavailable or HttpStatusCode.GatewayTimeout
or HttpStatusCode.InternalServerError;
}
private static TimeSpan GetRetryDelay(int attempt)
{
return TimeSpan.FromMilliseconds(500 * (attempt + 1));
}
private static PhotoboothConnectResponse Fail(string message)
{
return new PhotoboothConnectResponse
{
Message = message,
};
}
}