WebGLConnection.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. #if UNITY_WEBGL && !UNITY_EDITOR
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Runtime.InteropServices;
  6. using BestHTTP.Authentication;
  7. using BestHTTP.Extensions;
  8. namespace BestHTTP
  9. {
  10. delegate void OnWebGLRequestHandlerDelegate(int nativeId, int httpStatus, IntPtr pBuffer, int length, int zero);
  11. delegate void OnWebGLBufferDelegate(int nativeId, IntPtr pBuffer, int length);
  12. delegate void OnWebGLProgressDelegate(int nativeId, int downloaded, int total);
  13. delegate void OnWebGLErrorDelegate(int nativeId, string error);
  14. delegate void OnWebGLTimeoutDelegate(int nativeId);
  15. delegate void OnWebGLAbortedDelegate(int nativeId);
  16. internal sealed class WebGLConnection : ConnectionBase
  17. {
  18. static Dictionary<int, WebGLConnection> Connections = new Dictionary<int, WebGLConnection>(4);
  19. int NativeId;
  20. BufferPoolMemoryStream Stream;
  21. public WebGLConnection(string serverAddress)
  22. : base(serverAddress, false)
  23. {
  24. XHR_SetLoglevel((byte)HTTPManager.Logger.Level);
  25. }
  26. internal override void Abort(HTTPConnectionStates newState)
  27. {
  28. State = newState;
  29. switch (State)
  30. {
  31. case HTTPConnectionStates.TimedOut: TimedOutStart = DateTime.UtcNow; break;
  32. }
  33. XHR_Abort(this.NativeId);
  34. }
  35. protected override void ThreadFunc(object param /*null*/)
  36. {
  37. // XmlHttpRequest setup
  38. this.NativeId = XHR_Create(HTTPRequest.MethodNames[(byte)CurrentRequest.MethodType],
  39. CurrentRequest.CurrentUri.OriginalString,
  40. CurrentRequest.Credentials != null ? CurrentRequest.Credentials.UserName : null,
  41. CurrentRequest.Credentials != null ? CurrentRequest.Credentials.Password : null,
  42. CurrentRequest.WithCredentials ? 1 : 0);
  43. Connections.Add(NativeId, this);
  44. CurrentRequest.EnumerateHeaders((header, values) =>
  45. {
  46. if (header != "Content-Length")
  47. for (int i = 0; i < values.Count; ++i)
  48. XHR_SetRequestHeader(NativeId, header, values[i]);
  49. }, /*callBeforeSendCallback:*/ true);
  50. byte[] body = CurrentRequest.GetEntityBody();
  51. XHR_SetResponseHandler(NativeId, WebGLConnection.OnResponse, WebGLConnection.OnError, WebGLConnection.OnTimeout, WebGLConnection.OnAborted);
  52. // Setting OnUploadProgress result in an addEventListener("progress", ...) call making the request non-simple.
  53. // https://forum.unity.com/threads/best-http-released.200006/page-49#post-3696220
  54. XHR_SetProgressHandler(NativeId, WebGLConnection.OnDownloadProgress, CurrentRequest.OnUploadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnUploadProgress);
  55. XHR_SetTimeout(NativeId, (uint)(CurrentRequest.ConnectTimeout.TotalMilliseconds + CurrentRequest.Timeout.TotalMilliseconds));
  56. XHR_Send(NativeId, body, body != null ? body.Length : 0);
  57. }
  58. #region Callback Implementations
  59. void OnResponse(int httpStatus, byte[] buffer, int bufferLength)
  60. {
  61. try
  62. {
  63. using (var ms = new BufferPoolMemoryStream())
  64. {
  65. Stream = ms;
  66. XHR_GetStatusLine(NativeId, OnBufferCallback);
  67. XHR_GetResponseHeaders(NativeId, OnBufferCallback);
  68. if (buffer != null && bufferLength > 0)
  69. ms.Write(buffer, 0, bufferLength);
  70. ms.Seek(0L, SeekOrigin.Begin);
  71. var internalBuffer = ms.GetBuffer();
  72. string tmp = System.Text.Encoding.UTF8.GetString(internalBuffer);
  73. HTTPManager.Logger.Information(this.NativeId + " OnResponse - full response ", tmp);
  74. SupportedProtocols protocol = CurrentRequest.ProtocolHandler == SupportedProtocols.Unknown ? HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri) : CurrentRequest.ProtocolHandler;
  75. CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false);
  76. CurrentRequest.Response.Receive(buffer != null && bufferLength > 0 ? (int)bufferLength : -1, true);
  77. if (CurrentRequest.IsCookiesEnabled)
  78. BestHTTP.Cookies.CookieJar.Set(CurrentRequest.Response);
  79. }
  80. }
  81. catch (Exception e)
  82. {
  83. HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e);
  84. if (CurrentRequest != null)
  85. {
  86. // Something gone bad, Response must be null!
  87. CurrentRequest.Response = null;
  88. switch (State)
  89. {
  90. case HTTPConnectionStates.AbortRequested:
  91. CurrentRequest.State = HTTPRequestStates.Aborted;
  92. break;
  93. case HTTPConnectionStates.TimedOut:
  94. CurrentRequest.State = HTTPRequestStates.TimedOut;
  95. break;
  96. default:
  97. CurrentRequest.Exception = e;
  98. CurrentRequest.State = HTTPRequestStates.Error;
  99. break;
  100. }
  101. }
  102. }
  103. finally
  104. {
  105. Connections.Remove(NativeId);
  106. Stream = null;
  107. if (CurrentRequest != null)
  108. lock (HTTPManager.Locker)
  109. {
  110. State = HTTPConnectionStates.Closed;
  111. if (CurrentRequest.State == HTTPRequestStates.Processing)
  112. {
  113. if (CurrentRequest.Response != null)
  114. CurrentRequest.State = HTTPRequestStates.Finished;
  115. else
  116. CurrentRequest.State = HTTPRequestStates.Error;
  117. }
  118. }
  119. LastProcessTime = DateTime.UtcNow;
  120. if (OnConnectionRecycled != null)
  121. RecycleNow();
  122. XHR_Release(NativeId);
  123. }
  124. }
  125. void OnBuffer(byte[] buffer, int bufferLength)
  126. {
  127. if (Stream != null)
  128. {
  129. Stream.Write(buffer, 0, bufferLength);
  130. //Stream.Write(new byte[2] { HTTPResponse.CR, HTTPResponse.LF }, 0, 2);
  131. Stream.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length);
  132. }
  133. }
  134. void OnDownloadProgress(int down, int total)
  135. {
  136. CurrentRequest.Downloaded = down;
  137. CurrentRequest.DownloadLength = total;
  138. CurrentRequest.DownloadProgressChanged = true;
  139. }
  140. void OnUploadProgress(int up, int total)
  141. {
  142. CurrentRequest.Uploaded = up;
  143. CurrentRequest.UploadLength = total;
  144. CurrentRequest.UploadProgressChanged = true;
  145. }
  146. void OnError(string error)
  147. {
  148. HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnError", error);
  149. Connections.Remove(NativeId);
  150. Stream = null;
  151. if (CurrentRequest != null)
  152. lock (HTTPManager.Locker)
  153. {
  154. State = HTTPConnectionStates.Closed;
  155. CurrentRequest.State = HTTPRequestStates.Error;
  156. CurrentRequest.Exception = new Exception(error);
  157. }
  158. LastProcessTime = DateTime.UtcNow;
  159. if (OnConnectionRecycled != null)
  160. RecycleNow();
  161. XHR_Release(NativeId);
  162. }
  163. void OnTimeout()
  164. {
  165. HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnResponse", string.Empty);
  166. Connections.Remove(NativeId);
  167. Stream = null;
  168. if (CurrentRequest != null)
  169. lock (HTTPManager.Locker)
  170. {
  171. State = HTTPConnectionStates.Closed;
  172. CurrentRequest.State = HTTPRequestStates.TimedOut;
  173. }
  174. LastProcessTime = DateTime.UtcNow;
  175. if (OnConnectionRecycled != null)
  176. RecycleNow();
  177. XHR_Release(NativeId);
  178. }
  179. void OnAborted()
  180. {
  181. HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnAborted", string.Empty);
  182. Connections.Remove(NativeId);
  183. Stream = null;
  184. if (CurrentRequest != null)
  185. lock (HTTPManager.Locker)
  186. {
  187. State = HTTPConnectionStates.Closed;
  188. CurrentRequest.State = HTTPRequestStates.Aborted;
  189. }
  190. LastProcessTime = DateTime.UtcNow;
  191. if (OnConnectionRecycled != null)
  192. RecycleNow();
  193. XHR_Release(NativeId);
  194. }
  195. #endregion
  196. #region WebGL Static Callbacks
  197. [AOT.MonoPInvokeCallback(typeof(OnWebGLRequestHandlerDelegate))]
  198. static void OnResponse(int nativeId, int httpStatus, IntPtr pBuffer, int length, int err)
  199. {
  200. HTTPManager.Logger.Information("WebGLConnection - OnResponse", string.Format("{0} {1} {2} {3}", nativeId, httpStatus, length, err));
  201. WebGLConnection conn = null;
  202. if (!Connections.TryGetValue(nativeId, out conn))
  203. {
  204. HTTPManager.Logger.Error("WebGLConnection - OnResponse", "No WebGL connection found for nativeId: " + nativeId.ToString());
  205. return;
  206. }
  207. byte[] buffer = VariableSizedBufferPool.Get(length, true);
  208. // Copy data from the 'unmanaged' memory to managed memory. Buffer will be reclaimed by the GC.
  209. Marshal.Copy(pBuffer, buffer, 0, length);
  210. conn.OnResponse(httpStatus, buffer, length);
  211. VariableSizedBufferPool.Release(buffer);
  212. }
  213. [AOT.MonoPInvokeCallback(typeof(OnWebGLBufferDelegate))]
  214. static void OnBufferCallback(int nativeId, IntPtr pBuffer, int length)
  215. {
  216. WebGLConnection conn = null;
  217. if (!Connections.TryGetValue(nativeId, out conn))
  218. {
  219. HTTPManager.Logger.Error("WebGLConnection - OnBufferCallback", "No WebGL connection found for nativeId: " + nativeId.ToString());
  220. return;
  221. }
  222. byte[] buffer = VariableSizedBufferPool.Get(length, true);
  223. // Copy data from the 'unmanaged' memory to managed memory. Buffer will be reclaimed by the GC.
  224. Marshal.Copy(pBuffer, buffer, 0, length);
  225. conn.OnBuffer(buffer, length);
  226. VariableSizedBufferPool.Release(buffer);
  227. }
  228. [AOT.MonoPInvokeCallback(typeof(OnWebGLProgressDelegate))]
  229. static void OnDownloadProgress(int nativeId, int downloaded, int total)
  230. {
  231. HTTPManager.Logger.Information(nativeId + " OnDownloadProgress", downloaded.ToString() + " / " + total.ToString());
  232. WebGLConnection conn = null;
  233. if (!Connections.TryGetValue(nativeId, out conn))
  234. {
  235. HTTPManager.Logger.Error("WebGLConnection - OnDownloadProgress", "No WebGL connection found for nativeId: " + nativeId.ToString());
  236. return;
  237. }
  238. conn.OnDownloadProgress(downloaded, total);
  239. }
  240. [AOT.MonoPInvokeCallback(typeof(OnWebGLProgressDelegate))]
  241. static void OnUploadProgress(int nativeId, int uploaded, int total)
  242. {
  243. HTTPManager.Logger.Information(nativeId + " OnUploadProgress", uploaded.ToString() + " / " + total.ToString());
  244. WebGLConnection conn = null;
  245. if (!Connections.TryGetValue(nativeId, out conn))
  246. {
  247. HTTPManager.Logger.Error("WebGLConnection - OnUploadProgress", "No WebGL connection found for nativeId: " + nativeId.ToString());
  248. return;
  249. }
  250. conn.OnUploadProgress(uploaded, total);
  251. }
  252. [AOT.MonoPInvokeCallback(typeof(OnWebGLErrorDelegate))]
  253. static void OnError(int nativeId, string error)
  254. {
  255. WebGLConnection conn = null;
  256. if (!Connections.TryGetValue(nativeId, out conn))
  257. {
  258. HTTPManager.Logger.Error("WebGLConnection - OnError", "No WebGL connection found for nativeId: " + nativeId.ToString() + " Error: " + error);
  259. return;
  260. }
  261. conn.OnError(error);
  262. }
  263. [AOT.MonoPInvokeCallback(typeof(OnWebGLTimeoutDelegate))]
  264. static void OnTimeout(int nativeId)
  265. {
  266. WebGLConnection conn = null;
  267. if (!Connections.TryGetValue(nativeId, out conn))
  268. {
  269. HTTPManager.Logger.Error("WebGLConnection - OnTimeout", "No WebGL connection found for nativeId: " + nativeId.ToString());
  270. return;
  271. }
  272. conn.OnTimeout();
  273. }
  274. [AOT.MonoPInvokeCallback(typeof(OnWebGLAbortedDelegate))]
  275. static void OnAborted(int nativeId)
  276. {
  277. WebGLConnection conn = null;
  278. if (!Connections.TryGetValue(nativeId, out conn))
  279. {
  280. HTTPManager.Logger.Error("WebGLConnection - OnAborted", "No WebGL connection found for nativeId: " + nativeId.ToString());
  281. return;
  282. }
  283. conn.OnAborted();
  284. }
  285. #endregion
  286. #region WebGL Interface
  287. [DllImport("__Internal")]
  288. private static extern int XHR_Create(string method, string url, string userName, string passwd, int withCredentials);
  289. /// <summary>
  290. /// Is an unsigned long representing the number of milliseconds a request can take before automatically being terminated. A value of 0 (which is the default) means there is no timeout.
  291. /// </summary>
  292. [DllImport("__Internal")]
  293. private static extern void XHR_SetTimeout(int nativeId, uint timeout);
  294. [DllImport("__Internal")]
  295. private static extern void XHR_SetRequestHeader(int nativeId, string header, string value);
  296. [DllImport("__Internal")]
  297. private static extern void XHR_SetResponseHandler(int nativeId, OnWebGLRequestHandlerDelegate onresponse, OnWebGLErrorDelegate onerror, OnWebGLTimeoutDelegate ontimeout, OnWebGLAbortedDelegate onabort);
  298. [DllImport("__Internal")]
  299. private static extern void XHR_SetProgressHandler(int nativeId, OnWebGLProgressDelegate onDownloadProgress, OnWebGLProgressDelegate onUploadProgress);
  300. [DllImport("__Internal")]
  301. private static extern void XHR_Send(int nativeId, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] body, int length);
  302. [DllImport("__Internal")]
  303. private static extern void XHR_GetResponseHeaders(int nativeId, OnWebGLBufferDelegate callback);
  304. [DllImport("__Internal")]
  305. private static extern void XHR_GetStatusLine(int nativeId, OnWebGLBufferDelegate callback);
  306. [DllImport("__Internal")]
  307. private static extern void XHR_Abort(int nativeId);
  308. [DllImport("__Internal")]
  309. private static extern void XHR_Release(int nativeId);
  310. [DllImport("__Internal")]
  311. private static extern void XHR_SetLoglevel(int logLevel);
  312. #endregion
  313. }
  314. }
  315. #endif