using Logging; using Utils; namespace Core { public interface IHttp { T OnClient(Func action); T OnClient(Func action, string description); T OnClient(Func action, Retry retry); IEndpoint CreateEndpoint(Address address, string baseUrl, string? logAlias = null); } internal class Http : IHttp { private static object lockLock = new object(); private static readonly Dictionary httpLocks = new Dictionary(); private readonly ILog log; private readonly ITimeSet timeSet; private readonly Action onClientCreated; private readonly string id; internal Http(string id, ILog log, ITimeSet timeSet) : this(id, log, timeSet, DoNothing) { } internal Http(string id, ILog log, ITimeSet timeSet, Action onClientCreated) { this.id = id; this.log = log; this.timeSet = timeSet; this.onClientCreated = onClientCreated; } public T OnClient(Func action) { return OnClient(action, GetDescription()); } public T OnClient(Func action, string description) { var retry = new Retry(description, timeSet.HttpRetryTimeout(), timeSet.HttpCallRetryDelay(), f => { }); return OnClient(action, retry); } public T OnClient(Func action, Retry retry) { var client = GetClient(); return LockRetry(() => { return action(client); }, retry); } public IEndpoint CreateEndpoint(Address address, string baseUrl, string? logAlias = null) { return new Endpoint(log, this, address, baseUrl, logAlias); } private string GetDescription() { return DebugStack.GetCallerName(skipFrames: 2); } private T LockRetry(Func operation, Retry retry) { var httpLock = GetLock(); lock (httpLock) { return retry.Run(operation); } } private object GetLock() { lock (lockLock) // I had to. { if (!httpLocks.ContainsKey(id)) httpLocks.Add(id, new object()); return httpLocks[id]; } } private HttpClient GetClient() { var client = new HttpClient(); client.Timeout = timeSet.HttpCallTimeout(); onClientCreated(client); return client; } private static void DoNothing(HttpClient client) { } } }