WebGLConnection.cs 14 KB

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