HTTPManager.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. using System;
  2. using System.Collections.Generic;
  3. #if !BESTHTTP_DISABLE_CACHING
  4. using BestHTTP.Caching;
  5. #endif
  6. using BestHTTP.Core;
  7. using BestHTTP.Extensions;
  8. using BestHTTP.Logger;
  9. using BestHTTP.PlatformSupport.Memory;
  10. #if !BESTHTTP_DISABLE_COOKIES
  11. using BestHTTP.Cookies;
  12. #endif
  13. using BestHTTP.Connections;
  14. namespace BestHTTP
  15. {
  16. public enum ShutdownTypes
  17. {
  18. Running,
  19. Gentle,
  20. Immediate
  21. }
  22. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  23. public delegate Connections.TLS.AbstractTls13Client TlsClientFactoryDelegate(HTTPRequest request, List<SecureProtocol.Org.BouncyCastle.Tls.ProtocolName> protocols);
  24. #endif
  25. public delegate System.Security.Cryptography.X509Certificates.X509Certificate ClientCertificateSelector(HTTPRequest request, string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection localCertificates, System.Security.Cryptography.X509Certificates.X509Certificate remoteCertificate, string[] acceptableIssuers);
  26. /// <summary>
  27. ///
  28. /// </summary>
  29. [BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
  30. public static partial class HTTPManager
  31. {
  32. // Static constructor. Setup default values
  33. static HTTPManager()
  34. {
  35. MaxConnectionPerServer = 6;
  36. KeepAliveDefaultValue = true;
  37. MaxPathLength = 255;
  38. MaxConnectionIdleTime = TimeSpan.FromSeconds(20);
  39. #if !BESTHTTP_DISABLE_COOKIES
  40. #if UNITY_WEBGL && !UNITY_EDITOR
  41. // Under webgl when IsCookiesEnabled is true, it will set the withCredentials flag for the XmlHTTPRequest
  42. // and that's different from the default behavior.
  43. // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials
  44. IsCookiesEnabled = false;
  45. #else
  46. IsCookiesEnabled = true;
  47. #endif
  48. #endif
  49. CookieJarSize = 10 * 1024 * 1024;
  50. EnablePrivateBrowsing = false;
  51. ConnectTimeout = TimeSpan.FromSeconds(20);
  52. RequestTimeout = TimeSpan.FromSeconds(60);
  53. // Set the default logger mechanism
  54. logger = new BestHTTP.Logger.ThreadedLogger();
  55. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  56. UseAlternateSSLDefaultValue = true;
  57. #endif
  58. #if NETFX_CORE
  59. IOService = new PlatformSupport.FileSystem.NETFXCOREIOService();
  60. //#elif UNITY_WEBGL && !UNITY_EDITOR
  61. // IOService = new PlatformSupport.FileSystem.WebGLIOService();
  62. #else
  63. IOService = new PlatformSupport.FileSystem.DefaultIOService();
  64. #endif
  65. }
  66. #if (!UNITY_WEBGL || UNITY_EDITOR) && !BESTHTTP_DISABLE_ALTERNATE_SSL && !BESTHTTP_DISABLE_HTTP2
  67. /// <summary>
  68. /// HTTP/2 settings
  69. /// </summary>
  70. public static Connections.HTTP2.HTTP2PluginSettings HTTP2Settings = new Connections.HTTP2.HTTP2PluginSettings();
  71. #endif
  72. #region Global Options
  73. /// <summary>
  74. /// The maximum active TCP connections that the client will maintain to a server. Default value is 6. Minimum value is 1.
  75. /// </summary>
  76. public static byte MaxConnectionPerServer
  77. {
  78. get{ return maxConnectionPerServer; }
  79. set
  80. {
  81. if (value <= 0)
  82. throw new ArgumentOutOfRangeException("MaxConnectionPerServer must be greater than 0!");
  83. bool isGrowing = value > maxConnectionPerServer;
  84. maxConnectionPerServer = value;
  85. // If the allowed connections per server is growing, go through all hosts and try to send out queueud requests.
  86. if (isGrowing)
  87. HostManager.TryToSendQueuedRequests();
  88. }
  89. }
  90. private static byte maxConnectionPerServer;
  91. /// <summary>
  92. /// Default value of a HTTP request's IsKeepAlive value. Default value is true. If you make rare request to the server it should be changed to false.
  93. /// </summary>
  94. public static bool KeepAliveDefaultValue { get; set; }
  95. #if !BESTHTTP_DISABLE_CACHING
  96. /// <summary>
  97. /// Set to true, if caching is prohibited.
  98. /// </summary>
  99. public static bool IsCachingDisabled { get; set; }
  100. #endif
  101. /// <summary>
  102. /// How many time must be passed to destroy that connection after a connection finished its last request. Its default value is 20 seconds.
  103. /// </summary>
  104. public static TimeSpan MaxConnectionIdleTime { get; set; }
  105. #if !BESTHTTP_DISABLE_COOKIES
  106. /// <summary>
  107. /// Set to false to disable all Cookie. It's default value is true.
  108. /// </summary>
  109. public static bool IsCookiesEnabled { get; set; }
  110. #endif
  111. /// <summary>
  112. /// Size of the Cookie Jar in bytes. It's default value is 10485760 (10 MB).
  113. /// </summary>
  114. public static uint CookieJarSize { get; set; }
  115. /// <summary>
  116. /// If this property is set to true, then new cookies treated as session cookies and these cookies are not saved to disk. Its default value is false;
  117. /// </summary>
  118. public static bool EnablePrivateBrowsing { get; set; }
  119. /// <summary>
  120. /// Global, default value of the HTTPRequest's ConnectTimeout property. If set to TimeSpan.Zero or lower, no connect timeout logic is executed. Default value is 20 seconds.
  121. /// </summary>
  122. public static TimeSpan ConnectTimeout { get; set; }
  123. /// <summary>
  124. /// Global, default value of the HTTPRequest's Timeout property. Default value is 60 seconds.
  125. /// </summary>
  126. public static TimeSpan RequestTimeout { get; set; }
  127. /// <summary>
  128. /// By default the plugin will save all cache and cookie data under the path returned by Application.persistentDataPath.
  129. /// You can assign a function to this delegate to return a custom root path to define a new path.
  130. /// <remarks>This delegate will be called on a non Unity thread!</remarks>
  131. /// </summary>
  132. public static System.Func<string> RootCacheFolderProvider { get; set; }
  133. #if !BESTHTTP_DISABLE_PROXY
  134. /// <summary>
  135. /// The global, default proxy for all HTTPRequests. The HTTPRequest's Proxy still can be changed per-request. Default value is null.
  136. /// </summary>
  137. public static Proxy Proxy { get; set; }
  138. #endif
  139. /// <summary>
  140. /// Heartbeat manager to use less threads in the plugin. The heartbeat updates are called from the OnUpdate function.
  141. /// </summary>
  142. public static HeartbeatManager Heartbeats
  143. {
  144. get
  145. {
  146. if (heartbeats == null)
  147. heartbeats = new HeartbeatManager();
  148. return heartbeats;
  149. }
  150. }
  151. private static HeartbeatManager heartbeats;
  152. /// <summary>
  153. /// A basic BestHTTP.Logger.ILogger implementation to be able to log intelligently additional informations about the plugin's internal mechanism.
  154. /// </summary>
  155. public static BestHTTP.Logger.ILogger Logger
  156. {
  157. get
  158. {
  159. // Make sure that it has a valid logger instance.
  160. if (logger == null)
  161. {
  162. logger = new ThreadedLogger();
  163. logger.Level = Loglevels.None;
  164. }
  165. return logger;
  166. }
  167. set { logger = value; }
  168. }
  169. private static BestHTTP.Logger.ILogger logger;
  170. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  171. public static TlsClientFactoryDelegate TlsClientFactory;
  172. public static Connections.TLS.AbstractTls13Client DefaultTlsClientFactory(HTTPRequest request, List<SecureProtocol.Org.BouncyCastle.Tls.ProtocolName> protocols)
  173. {
  174. // http://tools.ietf.org/html/rfc3546#section-3.1
  175. // -It is RECOMMENDED that clients include an extension of type "server_name" in the client hello whenever they locate a server by a supported name type.
  176. // -Literal IPv4 and IPv6 addresses are not permitted in "HostName".
  177. // User-defined list has a higher priority
  178. List<SecureProtocol.Org.BouncyCastle.Tls.ServerName> hostNames = null;
  179. // If there's no user defined one and the host isn't an IP address, add the default one
  180. if (!request.CurrentUri.IsHostIsAnIPAddress())
  181. {
  182. hostNames = new List<SecureProtocol.Org.BouncyCastle.Tls.ServerName>(1);
  183. hostNames.Add(new SecureProtocol.Org.BouncyCastle.Tls.ServerName(0, System.Text.Encoding.UTF8.GetBytes(request.CurrentUri.Host)));
  184. }
  185. return new Connections.TLS.DefaultTls13Client(request, hostNames, protocols);
  186. }
  187. /// <summary>
  188. /// The default value for the HTTPRequest's UseAlternateSSL property.
  189. /// </summary>
  190. public static bool UseAlternateSSLDefaultValue { get; set; }
  191. #endif
  192. #if !NETFX_CORE
  193. public static Func<HTTPRequest, System.Security.Cryptography.X509Certificates.X509Certificate, System.Security.Cryptography.X509Certificates.X509Chain, System.Net.Security.SslPolicyErrors, bool> DefaultCertificationValidator;
  194. public static ClientCertificateSelector ClientCertificationProvider;
  195. #endif
  196. /// <summary>
  197. /// TCP Client's send buffer size.
  198. /// </summary>
  199. public static int? SendBufferSize;
  200. /// <summary>
  201. /// TCP Client's receive buffer size.
  202. /// </summary>
  203. public static int? ReceiveBufferSize;
  204. /// <summary>
  205. /// An IIOService implementation to handle filesystem operations.
  206. /// </summary>
  207. public static PlatformSupport.FileSystem.IIOService IOService;
  208. /// <summary>
  209. /// On most systems the maximum length of a path is around 255 character. If a cache entity's path is longer than this value it doesn't get cached. There no platform independent API to query the exact value on the current system, but it's
  210. /// exposed here and can be overridden. It's default value is 255.
  211. /// </summary>
  212. internal static int MaxPathLength { get; set; }
  213. /// <summary>
  214. /// User-agent string that will be sent with each requests.
  215. /// </summary>
  216. public static string UserAgent = "BestHTTP/2 v2.6.2";
  217. /// <summary>
  218. /// It's true if the application is quitting and the plugin is shutting down itself.
  219. /// </summary>
  220. public static bool IsQuitting { get { return _isQuitting; } private set { _isQuitting = value; } }
  221. private static volatile bool _isQuitting;
  222. #endregion
  223. #region Manager variables
  224. private static bool IsSetupCalled;
  225. #endregion
  226. #region Public Interface
  227. public static void Setup()
  228. {
  229. if (IsSetupCalled)
  230. return;
  231. IsSetupCalled = true;
  232. IsQuitting = false;
  233. HTTPManager.Logger.Information("HTTPManager", "Setup called! UserAgent: " + UserAgent);
  234. HTTPUpdateDelegator.CheckInstance();
  235. #if !BESTHTTP_DISABLE_CACHING
  236. HTTPCacheService.CheckSetup();
  237. #endif
  238. #if !BESTHTTP_DISABLE_COOKIES
  239. Cookies.CookieJar.SetupFolder();
  240. Cookies.CookieJar.Load();
  241. #endif
  242. HostManager.Load();
  243. }
  244. public static HTTPRequest SendRequest(string url, OnRequestFinishedDelegate callback)
  245. {
  246. return SendRequest(new HTTPRequest(new Uri(url), HTTPMethods.Get, callback));
  247. }
  248. public static HTTPRequest SendRequest(string url, HTTPMethods methodType, OnRequestFinishedDelegate callback)
  249. {
  250. return SendRequest(new HTTPRequest(new Uri(url), methodType, callback));
  251. }
  252. public static HTTPRequest SendRequest(string url, HTTPMethods methodType, bool isKeepAlive, OnRequestFinishedDelegate callback)
  253. {
  254. return SendRequest(new HTTPRequest(new Uri(url), methodType, isKeepAlive, callback));
  255. }
  256. public static HTTPRequest SendRequest(string url, HTTPMethods methodType, bool isKeepAlive, bool disableCache, OnRequestFinishedDelegate callback)
  257. {
  258. return SendRequest(new HTTPRequest(new Uri(url), methodType, isKeepAlive, disableCache, callback));
  259. }
  260. public static HTTPRequest SendRequest(HTTPRequest request)
  261. {
  262. if (!IsSetupCalled)
  263. Setup();
  264. if (request.IsCancellationRequested || IsQuitting)
  265. return request;
  266. #if !BESTHTTP_DISABLE_CACHING
  267. // If possible load the full response from cache.
  268. if (Caching.HTTPCacheService.IsCachedEntityExpiresInTheFuture(request))
  269. {
  270. DateTime started = DateTime.Now;
  271. PlatformSupport.Threading.ThreadedRunner.RunShortLiving<HTTPRequest>((req) =>
  272. {
  273. if (Connections.ConnectionHelper.TryLoadAllFromCache("HTTPManager", req, req.Context))
  274. {
  275. req.Timing.Add("Full Cache Load", DateTime.Now - started);
  276. req.State = HTTPRequestStates.Finished;
  277. }
  278. else
  279. {
  280. // If for some reason it couldn't load we place back the request to the queue.
  281. request.State = HTTPRequestStates.Queued;
  282. RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(request, RequestEvents.Resend));
  283. }
  284. }, request);
  285. }
  286. else
  287. #endif
  288. {
  289. request.State = HTTPRequestStates.Queued;
  290. RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(request, RequestEvents.Resend));
  291. }
  292. return request;
  293. }
  294. #endregion
  295. #region Internal Helper Functions
  296. /// <summary>
  297. /// Will return where the various caches should be saved.
  298. /// </summary>
  299. public static string GetRootCacheFolder()
  300. {
  301. try
  302. {
  303. if (RootCacheFolderProvider != null)
  304. return RootCacheFolderProvider();
  305. }
  306. catch(Exception ex)
  307. {
  308. HTTPManager.Logger.Exception("HTTPManager", "GetRootCacheFolder", ex);
  309. }
  310. #if NETFX_CORE
  311. return Windows.Storage.ApplicationData.Current.LocalFolder.Path;
  312. #else
  313. return UnityEngine.Application.persistentDataPath;
  314. #endif
  315. }
  316. #if UNITY_EDITOR
  317. #if UNITY_2019_3_OR_NEWER
  318. [UnityEngine.RuntimeInitializeOnLoadMethod(UnityEngine.RuntimeInitializeLoadType.SubsystemRegistration)]
  319. #endif
  320. public static void ResetSetup()
  321. {
  322. IsSetupCalled = false;
  323. BufferedReadNetworkStream.ResetNetworkStats();
  324. HTTPManager.Logger.Information("HTTPManager", "Reset called!");
  325. }
  326. #endif
  327. #endregion
  328. #region MonoBehaviour Events (Called from HTTPUpdateDelegator)
  329. /// <summary>
  330. /// Update function that should be called regularly from a Unity event(Update, LateUpdate). Callbacks are dispatched from this function.
  331. /// </summary>
  332. public static void OnUpdate()
  333. {
  334. RequestEventHelper.ProcessQueue();
  335. ConnectionEventHelper.ProcessQueue();
  336. ProtocolEventHelper.ProcessQueue();
  337. PluginEventHelper.ProcessQueue();
  338. BestHTTP.Extensions.Timer.Process();
  339. if (heartbeats != null)
  340. heartbeats.Update();
  341. BufferPool.Maintain();
  342. }
  343. public static void OnQuit()
  344. {
  345. HTTPManager.Logger.Information("HTTPManager", "OnQuit called!");
  346. IsQuitting = true;
  347. AbortAll();
  348. #if !BESTHTTP_DISABLE_CACHING
  349. HTTPCacheService.SaveLibrary();
  350. #endif
  351. #if !BESTHTTP_DISABLE_COOKIES
  352. CookieJar.Persist();
  353. #endif
  354. OnUpdate();
  355. HostManager.Clear();
  356. Heartbeats.Clear();
  357. }
  358. public static void AbortAll()
  359. {
  360. HTTPManager.Logger.Information("HTTPManager", "AbortAll called!");
  361. // This is an immediate shutdown request!
  362. RequestEventHelper.Clear();
  363. ConnectionEventHelper.Clear();
  364. PluginEventHelper.Clear();
  365. ProtocolEventHelper.Clear();
  366. HostManager.Shutdown();
  367. ProtocolEventHelper.CancelActiveProtocols();
  368. }
  369. #endregion
  370. }
  371. }