123 lines
4.1 KiB
C#
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,
|
|
};
|
|
}
|
|
}
|