diff --git a/ScoutHelper/Managers/BearManager.cs b/ScoutHelper/Managers/BearManager.cs index 0b9d579..e774263 100644 --- a/ScoutHelper/Managers/BearManager.cs +++ b/ScoutHelper/Managers/BearManager.cs @@ -3,7 +3,6 @@ using System.Collections.Immutable; using System.IO; using System.Linq; -using System.Net.Http; using System.Threading.Tasks; using CSharpFunctionalExtensions; using Dalamud.Plugin.Services; @@ -19,7 +18,7 @@ namespace ScoutHelper.Managers; public class BearManager : IDisposable { private readonly IPluginLog _log; private readonly Configuration _conf; - private readonly HttpClient _httpClient = new(); + private readonly HttpClientGenerator _httpClientGenerator; private IDictionary MobIdToBearName { get; init; } @@ -27,11 +26,17 @@ public BearManager(IPluginLog log, Configuration conf, ScoutHelperOptions option _log = log; _conf = conf; + _httpClientGenerator = new HttpClientGenerator( + _log, + () => _conf.BearApiBaseUrl, + client => client.Timeout = _conf.BearApiTimeout + ); + MobIdToBearName = LoadData(options.BearDataFile); } public void Dispose() { - _httpClient.Dispose(); + _httpClientGenerator.Dispose(); GC.SuppressFinalize(this); } @@ -52,13 +57,8 @@ IEnumerable trainMobs return await HttpUtils.DoRequest( _log, - _httpClient, - _conf.BearApiBaseUrl, new BearApiTrainRequest(worldName, _conf.BearTrainName, highestPatch.BearName(), spawnPoints), - (client, content) => { - client.Timeout = _conf.BearApiTimeout; - return client.PostAsync(_conf.BearApiTrainPath, content); - }, + (content) => _httpClientGenerator.Client.PostAsync(_conf.BearApiTrainPath, content), bearResponse => new BearLinkData( $"{_conf.BearSiteTrainUrl}/{bearResponse.Trains.First().TrainId}", bearResponse.Trains.First().Password, diff --git a/ScoutHelper/Managers/TurtleManager.cs b/ScoutHelper/Managers/TurtleManager.cs index 6be7b4e..60e6477 100644 --- a/ScoutHelper/Managers/TurtleManager.cs +++ b/ScoutHelper/Managers/TurtleManager.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; using CSharpFunctionalExtensions; @@ -30,7 +29,7 @@ public partial class TurtleManager : IDisposable { private readonly IPluginLog _log; private readonly Configuration _conf; private readonly IClientState _clientState; - private readonly HttpClient _httpClient = new(); + private readonly HttpClientGenerator _httpClientGenerator; private MobDict MobIdToTurtleId { get; } private TerritoryDict TerritoryIdToTurtleData { get; } @@ -52,12 +51,18 @@ MobManager mobManager _conf = conf; _clientState = clientState; + _httpClientGenerator = new HttpClientGenerator( + _log, + () => _conf.TurtleApiBaseUrl, + client => client.Timeout = _conf.TurtleApiTimeout + ); + (MobIdToTurtleId, TerritoryIdToTurtleData) = LoadData(options.TurtleDataFile, territoryManager, mobManager); } public void Dispose() { - _httpClient.Dispose(); + _httpClientGenerator.Dispose(); GC.SuppressFinalize(this); } @@ -88,8 +93,6 @@ public async Task UpdateCurrentSession(IList train) var httpResult = await HttpUtils.DoRequest( _log, - _httpClient, - _conf.TurtleApiBaseUrl, new TurtleTrainUpdateRequest( _currentCollabPassword, _clientState.PlayerTag().Where(_ => _conf.IncludeNameInTurtleSession), @@ -101,10 +104,7 @@ public async Task UpdateCurrentSession(IList train) mob.Position) ) ), - (client, content) => { - client.Timeout = _conf.TurtleApiTimeout; - return client.PatchAsync($"{_conf.TurtleApiTrainPath}/{_currentCollabSession}", content); - } + ( content) => _httpClientGenerator.Client.PatchAsync($"{_conf.TurtleApiTrainPath}/{_currentCollabSession}", content) ).TapError( error => { if (error.ErrorType == HttpErrorType.Timeout) { @@ -139,13 +139,8 @@ public async Task> GenerateTurtleLink( return await HttpUtils.DoRequest( _log, - _httpClient, - _conf.TurtleApiBaseUrl, TurtleTrainRequest.CreateRequest(spawnPoints), - (client, content) => { - client.Timeout = _conf.TurtleApiTimeout; - return client.PostAsync(_conf.TurtleApiTrainPath, content); - }, + ( content) => _httpClientGenerator.Client.PostAsync(_conf.TurtleApiTrainPath, content), trainResponse => TurtleLinkData.From(trainResponse, highestPatch) ) .HandleHttpError( diff --git a/ScoutHelper/Utils/HttpClientGenerator.cs b/ScoutHelper/Utils/HttpClientGenerator.cs new file mode 100644 index 0000000..0270955 --- /dev/null +++ b/ScoutHelper/Utils/HttpClientGenerator.cs @@ -0,0 +1,50 @@ +using System; +using System.Net.Http; +using Dalamud.Plugin.Services; + +namespace ScoutHelper.Utils; + +public class HttpClientGenerator : IDisposable { + private readonly IPluginLog _log; + private readonly Func _baseUrlSupplier; + private readonly Action _clientConfigurer; + + private HttpClient _client = new(); + + public HttpClient Client { + get { + var latestUrl = _baseUrlSupplier().AsUri(); + if (_client.BaseAddress != latestUrl) { + InitializeNewClient(); + } + + return _client; + } + } + + public HttpClientGenerator(IPluginLog log, Func baseUrlSupplier, Action clientConfigurer) { + _log = log; + _baseUrlSupplier = baseUrlSupplier; + _clientConfigurer = clientConfigurer; + + InitializeNewClient(); + } + + public void Dispose() { + _client.Dispose(); + + GC.SuppressFinalize(this); + } + + private void InitializeNewClient() { + var client = new HttpClient(); + client.BaseAddress = _baseUrlSupplier().AsUri(); + _log.Debug("generating a new http client for base address: {0:l}", client.BaseAddress.ToString()); + client.DefaultRequestHeaders.UserAgent.Add(Constants.UserAgent); + client.DefaultRequestHeaders.Accept.Add(Constants.MediaTypeJson); + _clientConfigurer(client); + + _client.Dispose(); + _client = client; + } +} diff --git a/ScoutHelper/Utils/HttpUtils.cs b/ScoutHelper/Utils/HttpUtils.cs index 5edfd4b..0e7209c 100644 --- a/ScoutHelper/Utils/HttpUtils.cs +++ b/ScoutHelper/Utils/HttpUtils.cs @@ -18,13 +18,11 @@ public static class HttpUtils { public static Task> DoRequest( IPluginLog log, - HttpClient client, - string baseUrl, T requestObject, - Func> requestAction, + Func> requestAction, Func responseTransform ) => - DoRequest(log, client, baseUrl, requestObject, requestAction) + DoRequest(log, requestObject, requestAction) .Then( result => result.Bind( responseJson => Utils.Try( @@ -36,20 +34,15 @@ Func responseTransform public static async Task> DoRequest( IPluginLog log, - HttpClient client, - string baseUrl, T requestObject, - Func> requestAction + Func> requestAction ) { try { var requestPayload = JsonConvert.SerializeObject(requestObject, JsonSerializerSettings); log.Debug("Request body: {0:l}", requestPayload); var requestContent = new StringContent(requestPayload, Encoding.UTF8, Constants.MediaTypeJson); - client.BaseAddress = new Uri(baseUrl); - client.DefaultRequestHeaders.UserAgent.Add(Constants.UserAgent); - client.DefaultRequestHeaders.Accept.Add(Constants.MediaTypeJson); - var response = await requestAction(client, requestContent); + var response = await requestAction(requestContent); log.Debug( "Request: {0:l}\n\nResponse: {1:l}", response.RequestMessage!.ToString(), diff --git a/ScoutHelper/Utils/Utils.cs b/ScoutHelper/Utils/Utils.cs index 7b35ced..963259b 100644 --- a/ScoutHelper/Utils/Utils.cs +++ b/ScoutHelper/Utils/Utils.cs @@ -144,5 +144,7 @@ private static IEnumerable Tokenize(string s) { public static bool ActualValuesEqualBecauseMicrosoftHasBrainDamage(object? objA, object? objB) => Equals(objA, objB) || Equals(JsonConvert.SerializeObject(objA), JsonConvert.SerializeObject(objB)); + public static Uri AsUri(this string str) => new(str); + #endregion }