HTTPManager.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. using Best.HTTP.Caching;
  2. using Best.HTTP.Cookies;
  3. using Best.HTTP.Hosts.Connections;
  4. using Best.HTTP.Hosts.Settings;
  5. using Best.HTTP.HostSetting;
  6. using Best.HTTP.Request.Authentication;
  7. using Best.HTTP.Request.Timings;
  8. using Best.HTTP.Shared.Extensions;
  9. using Best.HTTP.Shared.Logger;
  10. using Best.HTTP.Shared.PlatformSupport.Memory;
  11. using Best.HTTP.Shared.PlatformSupport.Text;
  12. using Best.HTTP.Shared.PlatformSupport.Threading;
  13. using System;
  14. using System.IO;
  15. namespace Best.HTTP.Shared
  16. {
  17. public enum ShutdownTypes
  18. {
  19. Running,
  20. Gentle,
  21. Immediate
  22. }
  23. public delegate void OnSetupFinishedDelegate();
  24. /// <summary>
  25. /// Global entry point to access and manage main services of the plugin.
  26. /// </summary>
  27. [Best.HTTP.Shared.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
  28. public static partial class HTTPManager
  29. {
  30. /// <summary>
  31. /// Static constructor. Setup default values.
  32. /// </summary>
  33. static HTTPManager()
  34. {
  35. PerHostSettings.Add("*", new HostSettings());
  36. // Set the default logger mechanism
  37. logger = new Best.HTTP.Shared.Logger.ThreadedLogger();
  38. IOService = new Best.HTTP.Shared.PlatformSupport.FileSystem.DefaultIOService();
  39. #if !UNITY_WEBGL || UNITY_EDITOR
  40. ProxyDetector = new HTTP.Proxies.Autodetect.ProxyDetector();
  41. #endif
  42. UserAgent = $"com.Tivadar.Best.HTTP v{typeof(HTTPManager)?.Assembly?.GetName()?.Version}/Unity {UnityEngine.Application.unityVersion}";
  43. }
  44. /// <summary>
  45. /// Delegate for the setup finished event.
  46. /// </summary>
  47. public static OnSetupFinishedDelegate OnSetupFinished;
  48. /// <summary>
  49. /// Instance of the per-host settings manager.
  50. /// </summary>
  51. public static HostSettingsManager PerHostSettings { get; private set; } = new HostSettingsManager();
  52. /// <summary>
  53. /// Cached DateTime value for cases where high resolution isn't needed.
  54. /// </summary>
  55. /// <remarks>Warning!! It must be used only on the main update thread!</remarks>
  56. public static DateTime CurrentFrameDateTime { get; private set; } = DateTime.Now;
  57. /// <summary>
  58. /// By default the plugin will save all cache and cookie data under the path returned by Application.persistentDataPath.
  59. /// You can assign a function to this delegate to return a custom root path to define a new path.
  60. /// <remarks>This delegate will be called on a non Unity thread!</remarks>
  61. /// </summary>
  62. public static Func<string> RootSaveFolderProvider { get; set; }
  63. #if !UNITY_WEBGL || UNITY_EDITOR
  64. public static HTTP.Proxies.Autodetect.ProxyDetector ProxyDetector {
  65. get => _proxyDetector;
  66. set {
  67. _proxyDetector?.Detach();
  68. _proxyDetector = value;
  69. }
  70. }
  71. private static HTTP.Proxies.Autodetect.ProxyDetector _proxyDetector;
  72. #endif
  73. /// <summary>
  74. /// The global, default proxy for all HTTPRequests. The HTTPRequest's Proxy still can be changed per-request. Default value is null.
  75. /// </summary>
  76. public static HTTP.Proxies.Proxy Proxy { get; set; }
  77. /// <summary>
  78. /// Heartbeat manager to use less threads in the plugin. The heartbeat updates are called from the OnUpdate function.
  79. /// </summary>
  80. public static HeartbeatManager Heartbeats
  81. {
  82. get
  83. {
  84. if (heartbeats == null)
  85. heartbeats = new HeartbeatManager();
  86. return heartbeats;
  87. }
  88. }
  89. private static HeartbeatManager heartbeats;
  90. /// <summary>
  91. /// A basic Best.HTTP.Logger.ILogger implementation to be able to log intelligently additional informations about the plugin's internal mechanism.
  92. /// </summary>
  93. public static Best.HTTP.Shared.Logger.ILogger Logger
  94. {
  95. get
  96. {
  97. // Make sure that it has a valid logger instance.
  98. if (logger == null)
  99. {
  100. logger = new ThreadedLogger();
  101. logger.Level = Loglevels.None;
  102. }
  103. return logger;
  104. }
  105. set { logger = value; }
  106. }
  107. private static Best.HTTP.Shared.Logger.ILogger logger;
  108. /// <summary>
  109. /// An IIOService implementation to handle filesystem operations.
  110. /// </summary>
  111. public static Best.HTTP.Shared.PlatformSupport.FileSystem.IIOService IOService;
  112. /// <summary>
  113. /// User-agent string that will be sent with each requests.
  114. /// </summary>
  115. public static string UserAgent;
  116. /// <summary>
  117. /// It's true if the application is quitting and the plugin is shutting down itself.
  118. /// </summary>
  119. public static bool IsQuitting { get { return _isQuitting; } private set { _isQuitting = value; } }
  120. private static volatile bool _isQuitting;
  121. public static string RootFolderName = "com.Tivadar.Best.HTTP.v3";
  122. /// <summary>
  123. /// The local content cache, maintained by the plugin. When set to a non-null value, Maintain called immediately on the cache.
  124. /// </summary>
  125. public static HTTPCache LocalCache
  126. {
  127. get => _httpCache;
  128. set
  129. {
  130. _httpCache?.Dispose();
  131. (_httpCache = value)?.Maintain(contentLength: 0, deleteLockedEntries: true, context: null);
  132. }
  133. }
  134. private static HTTPCache _httpCache;
  135. private static bool IsSetupCalled;
  136. /// <summary>
  137. /// Initializes the HTTPManager with default settings. This method should be called on Unity's main thread before using the HTTP plugin. By default it gets called by <see cref="HTTPUpdateDelegator"/>.
  138. /// </summary>
  139. public static void Setup()
  140. {
  141. if (IsSetupCalled)
  142. return;
  143. IsSetupCalled = true;
  144. IsQuitting = false;
  145. HTTPManager.Logger.Information("HTTPManager", "Setup called! UserAgent: " + UserAgent);
  146. HTTPUpdateDelegator.CheckInstance();
  147. if (LocalCache == null)
  148. {
  149. // this will trigger a maintain call too.
  150. LocalCache = new HTTPCache(new HTTPCacheOptions());
  151. }
  152. CookieJar.SetupFolder();
  153. CookieJar.Load();
  154. try
  155. {
  156. OnSetupFinished?.Invoke();
  157. }
  158. catch(Exception ex)
  159. {
  160. HTTPManager.logger.Exception(nameof(HTTPManager), "OnSetupFinished", ex, null);
  161. }
  162. }
  163. internal static HTTPRequest SendRequest(HTTPRequest request)
  164. {
  165. if (!IsSetupCalled)
  166. Setup();
  167. if (request.IsCancellationRequested || IsQuitting)
  168. return request;
  169. if (!request.DownloadSettings.DisableCache)
  170. {
  171. #if !UNITY_WEBGL || UNITY_EDITOR
  172. ThreadedRunner.RunShortLiving<HTTPRequest>((request) =>
  173. {
  174. #endif
  175. var hash = HTTPCache.CalculateHash(request.MethodType, request.CurrentUri);
  176. if (LocalCache.CanServeWithoutValidation(hash, ErrorTypeForValidation.None, request.Context))
  177. LocalCache.Redirect(request, hash);
  178. //RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(request, HTTPRequestStates.Queued, null));
  179. RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(request, RequestEvents.QueuedResend));
  180. #if !UNITY_WEBGL || UNITY_EDITOR
  181. }, request);
  182. #endif
  183. }
  184. else
  185. {
  186. //RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(request, HTTPRequestStates.Queued, null));
  187. RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(request, RequestEvents.QueuedResend));
  188. }
  189. return request;
  190. }
  191. /// <summary>
  192. /// Will return where the various caches should be saved.
  193. /// </summary>
  194. public static string GetRootSaveFolder()
  195. {
  196. try
  197. {
  198. if (RootSaveFolderProvider != null)
  199. return Path.Combine(RootSaveFolderProvider(), RootFolderName);
  200. }
  201. catch (Exception ex)
  202. {
  203. HTTPManager.Logger.Exception(nameof(HTTPManager), nameof(GetRootSaveFolder), ex);
  204. }
  205. #if UNITY_SWITCH && !UNITY_EDITOR
  206. throw new NotSupportedException(UnityEngine.Application.platform.ToString());
  207. #else
  208. return Path.Combine(UnityEngine.Application.persistentDataPath, RootFolderName);
  209. #endif
  210. }
  211. #if UNITY_EDITOR
  212. [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
  213. public static void ResetSetup()
  214. {
  215. IsSetupCalled = false;
  216. Profiler.Network.NetworkStatsCollector.ResetNetworkStats();
  217. #if !UNITY_WEBGL || UNITY_EDITOR
  218. PlatformSupport.Network.DNS.Cache.DNSCache.Clear();
  219. #endif
  220. DigestStore.Clear();
  221. PerHostSettings.Clear();
  222. PerHostSettings.Add("*", new HostSettings());
  223. LocalCache = null;
  224. HTTPManager.Logger.Information("HTTPManager", "Reset called!");
  225. }
  226. #endif
  227. /// <summary>
  228. /// Updates the HTTPManager. This method should be called regularly from a Unity event (e.g., Update, LateUpdate).
  229. /// It processes various events and callbacks and manages internal tasks.
  230. /// </summary>
  231. public static void OnUpdate()
  232. {
  233. try
  234. {
  235. CurrentFrameDateTime = DateTime.Now;
  236. using (new Unity.Profiling.ProfilerMarker(nameof(TimingEventHelper)).Auto())
  237. TimingEventHelper.ProcessQueue();
  238. using (new Unity.Profiling.ProfilerMarker(nameof(RequestEventHelper)).Auto())
  239. RequestEventHelper.ProcessQueue();
  240. using (new Unity.Profiling.ProfilerMarker(nameof(ConnectionEventHelper)).Auto())
  241. ConnectionEventHelper.ProcessQueue();
  242. if (heartbeats != null)
  243. {
  244. using (new Unity.Profiling.ProfilerMarker(nameof(HeartbeatManager)).Auto())
  245. heartbeats.Update();
  246. }
  247. using (new Unity.Profiling.ProfilerMarker(nameof(BufferPool)).Auto())
  248. BufferPool.Maintain();
  249. using (new Unity.Profiling.ProfilerMarker(nameof(StringBuilderPool)).Auto())
  250. StringBuilderPool.Maintain();
  251. #if BESTHTTP_PROFILE && UNITY_2021_2_OR_NEWER
  252. using (new Unity.Profiling.ProfilerMarker("Profile").Auto())
  253. {
  254. // Sent
  255. {
  256. long newNetworkBytesSent = Profiler.Network.NetworkStatsCollector.TotalNetworkBytesSent;
  257. var diff = newNetworkBytesSent - _lastNetworkBytesSent;
  258. _lastNetworkBytesSent = newNetworkBytesSent;
  259. Profiler.Network.NetworkStats.SentSinceLastFrame.Value = diff;
  260. Profiler.Network.NetworkStats.SentTotal.Value = newNetworkBytesSent;
  261. Profiler.Network.NetworkStats.BufferedToSend.Value = Profiler.Network.NetworkStatsCollector.BufferedToSend;
  262. }
  263. // Received
  264. {
  265. long newNetworkBytesReceived = Profiler.Network.NetworkStatsCollector.TotalNetworkBytesReceived;
  266. var diff = newNetworkBytesReceived - _lastNetworkBytesReceived;
  267. _lastNetworkBytesReceived = newNetworkBytesReceived;
  268. Profiler.Network.NetworkStats.ReceivedSinceLastFrame.Value = diff;
  269. Profiler.Network.NetworkStats.ReceivedTotal.Value = newNetworkBytesReceived;
  270. Profiler.Network.NetworkStats.ReceivedAndUnprocessed.Value = Profiler.Network.NetworkStatsCollector.ReceivedAndUnprocessed;
  271. }
  272. // Open/Total connections
  273. Profiler.Network.NetworkStats.OpenConnectionsCounter.Value = Profiler.Network.NetworkStatsCollector.OpenConnections;
  274. Profiler.Network.NetworkStats.TotalConnectionsCounter.Value = Profiler.Network.NetworkStatsCollector.TotalConnections;
  275. // Memory stats
  276. BufferPool.GetStatistics(ref bufferPoolStats);
  277. Profiler.Memory.MemoryStats.Borrowed.Value = bufferPoolStats.Borrowed;
  278. Profiler.Memory.MemoryStats.Pooled.Value = bufferPoolStats.PoolSize;
  279. Profiler.Memory.MemoryStats.CacheHits.Value = bufferPoolStats.GetBuffers;
  280. Profiler.Memory.MemoryStats.ArrayAllocations.Value = bufferPoolStats.ArrayAllocations;
  281. }
  282. #endif
  283. }
  284. catch (Exception ex)
  285. {
  286. HTTPManager.logger.Exception(nameof(HTTPManager), nameof(OnUpdate), ex);
  287. }
  288. }
  289. #if BESTHTTP_PROFILE && UNITY_2021_2_OR_NEWER
  290. private static long _lastNetworkBytesSent = 0;
  291. private static long _lastNetworkBytesReceived = 0;
  292. private static BufferPoolStats bufferPoolStats = default;
  293. #endif
  294. /// <summary>
  295. /// Shuts down the HTTPManager and performs cleanup operations. This method should be called when the application is quitting.
  296. /// </summary>
  297. public static void OnQuit()
  298. {
  299. HTTPManager.Logger.Information("HTTPManager", "OnQuit called!");
  300. IsQuitting = true;
  301. AbortAll();
  302. CookieJar.Persist();
  303. OnUpdate();
  304. HostManager.Clear();
  305. Heartbeats.Clear();
  306. DigestStore.Clear();
  307. }
  308. /// <summary>
  309. /// Aborts all ongoing HTTP requests and performs an immediate shutdown of the HTTPManager.
  310. /// </summary>
  311. public static void AbortAll()
  312. {
  313. HTTPManager.Logger.Information("HTTPManager", "AbortAll called!");
  314. // This is an immediate shutdown request!
  315. RequestEventHelper.Clear();
  316. ConnectionEventHelper.Clear();
  317. HostManager.Shutdown();
  318. }
  319. }
  320. }