using System; using System.Collections; using System.Net; using System.Threading; using UnityEngine; using XUnity.AutoTranslator.Plugin.Core.Configuration; namespace XUnity.AutoTranslator.Plugin.Core.Web { public abstract class KnownHttpEndpoint : IKnownEndpoint { private static readonly TimeSpan MaxUnusedLifespan = TimeSpan.FromSeconds( 50 ); private ServicePoint[] _servicePoints; private bool _isBusy = false; private UnityWebClient _client; private DateTime? _clientLastUse = null; public KnownHttpEndpoint() { } public bool IsBusy => _isBusy; public virtual bool SupportsLineSplitting { get { return false; } } protected void SetupServicePoints( params string[] endpoints ) { _servicePoints = new ServicePoint[ endpoints.Length ]; for( int i = 0 ; i < endpoints.Length ; i++ ) { var endpoint = endpoints[ i ]; var servicePoint = ServicePointManager.FindServicePoint( new Uri( endpoint ) ); _servicePoints[ i ] = servicePoint; } } public IEnumerator Translate( string untranslatedText, string from, string to, Action success, Action failure ) { _isBusy = true; try { var setup = OnBeforeTranslate( Settings.TranslationCount ); if( setup != null ) { while( setup.MoveNext() ) { yield return setup.Current; } } Logger.Current.Debug( "Starting translation for: " + untranslatedText ); DownloadResult downloadResult = null; try { var client = GetClient(); var url = GetServiceUrl( untranslatedText, from, to ); var request = GetRequestObject( untranslatedText, from, to ); ApplyHeaders( client.Headers ); if( request != null ) { downloadResult = client.GetDownloadResult( new Uri( url ), request ); } else { downloadResult = client.GetDownloadResult( new Uri( url ) ); } } catch( Exception e ) { Logger.Current.Error( e, "Error occurred while setting up translation request." ); } if( downloadResult != null ) { if( Features.SupportsCustomYieldInstruction ) { yield return downloadResult; } else { while( !downloadResult.IsCompleted ) { yield return new WaitForSeconds( 0.2f ); } } try { if( downloadResult.Succeeded && downloadResult.Result != null ) { if( TryExtractTranslated( downloadResult.Result, out var translatedText ) ) { Logger.Current.Debug( $"Translation for '{untranslatedText}' succeded. Result: {translatedText}" ); translatedText = translatedText ?? string.Empty; success( translatedText ); } else { Logger.Current.Error( "Error occurred while extracting translation." ); failure(); } } else { Logger.Current.Error( "Error occurred while retrieving translation." + Environment.NewLine + downloadResult.Error ); failure(); } } catch( Exception e ) { Logger.Current.Error( e, "Error occurred while retrieving translation." ); failure(); } } else { failure(); } } finally { _clientLastUse = DateTime.UtcNow; _isBusy = false; } } public virtual void OnUpdate() { if( !_isBusy && _clientLastUse.HasValue && DateTime.UtcNow - _clientLastUse > MaxUnusedLifespan && !_client.IsBusy && _servicePoints != null && _servicePoints.Length > 0 ) { Logger.Current.Debug( $"Closing service points because they were not used for {(int)MaxUnusedLifespan.TotalSeconds} seconds." ); _isBusy = true; _clientLastUse = null; ThreadPool.QueueUserWorkItem( delegate ( object state ) { // never do a job like this on the game loop thread try { foreach( var servicePoint in _servicePoints ) { servicePoint.CloseConnectionGroup( MyWebClient.ConnectionGroupName ); } } finally { _isBusy = false; } } ); } } public virtual bool ShouldGetSecondChanceAfterFailure() { return false; } public abstract string GetServiceUrl( string untranslatedText, string from, string to ); public abstract void ApplyHeaders( WebHeaderCollection headers ); public abstract bool TryExtractTranslated( string result, out string translated ); public virtual string GetRequestObject( string untranslatedText, string from, string to ) { return null; } public virtual void WriteCookies( HttpWebResponse response ) { } public virtual CookieContainer ReadCookies() { return null; } public virtual IEnumerator OnBeforeTranslate( int translationCount ) { return null; } public UnityWebClient GetClient() { if( _client == null ) { _client = new UnityWebClient( this ); _clientLastUse = DateTime.UtcNow; } return _client; } } }