123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- #if UNITY_WEBGL && !UNITY_EDITOR
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Runtime.InteropServices;
- #if !BESTHTTP_DISABLE_CACHING
- using BestHTTP.Caching;
- #endif
- using BestHTTP.Core;
- using BestHTTP.Extensions;
- using BestHTTP.Connections;
- using BestHTTP.PlatformSupport.Memory;
- namespace BestHTTP.Connections
- {
- delegate void OnWebGLRequestHandlerDelegate(int nativeId, int httpStatus, IntPtr pBuffer, int length, int zero);
- delegate void OnWebGLBufferDelegate(int nativeId, IntPtr pBuffer, int length);
- delegate void OnWebGLProgressDelegate(int nativeId, int downloaded, int total);
- delegate void OnWebGLErrorDelegate(int nativeId, string error);
- delegate void OnWebGLTimeoutDelegate(int nativeId);
- delegate void OnWebGLAbortedDelegate(int nativeId);
- internal sealed class WebGLConnection : ConnectionBase
- {
- static Dictionary<int, WebGLConnection> Connections = new Dictionary<int, WebGLConnection>(4);
- int NativeId;
- BufferSegmentStream Stream;
- public WebGLConnection(string serverAddress)
- : base(serverAddress, false)
- {
- XHR_SetLoglevel((byte)HTTPManager.Logger.Level);
- }
- public override void Shutdown(ShutdownTypes type)
- {
- base.Shutdown(type);
- XHR_Abort(this.NativeId);
- }
- protected override void ThreadFunc()
- {
- // XmlHttpRequest setup
- this.NativeId = XHR_Create(HTTPRequest.MethodNames[(byte)CurrentRequest.MethodType],
- CurrentRequest.CurrentUri.OriginalString,
- CurrentRequest.Credentials != null ? CurrentRequest.Credentials.UserName : null,
- CurrentRequest.Credentials != null ? CurrentRequest.Credentials.Password : null,
- CurrentRequest.WithCredentials ? 1 : 0);
- Connections.Add(NativeId, this);
- CurrentRequest.EnumerateHeaders((header, values) =>
- {
- if (!header.Equals("Content-Length"))
- for (int i = 0; i < values.Count; ++i)
- XHR_SetRequestHeader(NativeId, header, values[i]);
- }, /*callBeforeSendCallback:*/ true);
- XHR_SetResponseHandler(NativeId, WebGLConnection.OnResponse, WebGLConnection.OnError, WebGLConnection.OnTimeout, WebGLConnection.OnAborted);
- // Setting OnUploadProgress result in an addEventListener("progress", ...) call making the request non-simple.
- // https://forum.unity.com/threads/best-http-released.200006/page-49#post-3696220
- XHR_SetProgressHandler(NativeId,
- CurrentRequest.OnDownloadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnDownloadProgress,
- CurrentRequest.OnUploadProgress == null ? (OnWebGLProgressDelegate)null : WebGLConnection.OnUploadProgress);
- XHR_SetTimeout(NativeId, (uint)(CurrentRequest.ConnectTimeout.TotalMilliseconds + CurrentRequest.Timeout.TotalMilliseconds));
- byte[] body = CurrentRequest.GetEntityBody();
- int length = 0;
- bool releaseBodyBuffer = false;
- if (body == null)
- {
- var upStreamInfo = CurrentRequest.GetUpStream();
- if (upStreamInfo.Stream != null)
- {
- var internalBuffer = BufferPool.Get(upStreamInfo.Length > 0 ? upStreamInfo.Length : HTTPRequest.UploadChunkSize, true);
- using (BufferPoolMemoryStream ms = new BufferPoolMemoryStream(internalBuffer, 0, internalBuffer.Length, true, true, false, true))
- {
- var buffer = BufferPool.Get(HTTPRequest.UploadChunkSize, true);
- int readCount = -1;
- while ((readCount = upStreamInfo.Stream.Read(buffer, 0, buffer.Length)) > 0)
- ms.Write(buffer, 0, readCount);
- BufferPool.Release(buffer);
- length = (int)ms.Position;
- body = ms.GetBuffer();
- releaseBodyBuffer = true;
- }
- }
- }
- else
- {
- length = body.Length;
- }
- XHR_Send(NativeId, body, length);
- if (releaseBodyBuffer)
- BufferPool.Release(body);
- }
- #region Callback Implementations
- void OnResponse(int httpStatus, BufferSegment payload)
- {
- HTTPConnectionStates proposedConnectionState = HTTPConnectionStates.Processing;
- bool resendRequest = false;
- try
- {
- if (this.CurrentRequest.IsCancellationRequested)
- return;
- using (var ms = new BufferSegmentStream())
- {
- Stream = ms;
- XHR_GetStatusLine(NativeId, OnBufferCallback);
- XHR_GetResponseHeaders(NativeId, OnBufferCallback);
- if (payload != BufferSegment.Empty)
- ms.Write(payload);
- SupportedProtocols protocol = HTTPProtocolFactory.GetProtocolFromUri(CurrentRequest.CurrentUri);
- CurrentRequest.Response = HTTPProtocolFactory.Get(protocol, CurrentRequest, ms, CurrentRequest.UseStreaming, false);
- CurrentRequest.Response.Receive(payload != BufferSegment.Empty && payload.Count > 0 ? (int)payload.Count : -1, true);
- KeepAliveHeader keepAlive = null;
- ConnectionHelper.HandleResponse(this.ToString(), this.CurrentRequest, out resendRequest, out proposedConnectionState, ref keepAlive);
- }
- }
- catch (Exception e)
- {
- HTTPManager.Logger.Exception(this.NativeId + " WebGLConnection", "OnResponse", e, this.Context);
- if (this.ShutdownType == ShutdownTypes.Immediate)
- return;
- #if !BESTHTTP_DISABLE_CACHING
- if (this.CurrentRequest.UseStreaming)
- HTTPCacheService.DeleteEntity(this.CurrentRequest.CurrentUri);
- #endif
- // Something gone bad, Response must be null!
- this.CurrentRequest.Response = null;
- if (!this.CurrentRequest.IsCancellationRequested)
- {
- this.CurrentRequest.Exception = e;
- this.CurrentRequest.State = HTTPRequestStates.Error;
- }
- proposedConnectionState = HTTPConnectionStates.Closed;
- }
- finally
- {
- // Exit ASAP
- if (this.ShutdownType != ShutdownTypes.Immediate)
- {
- if (this.CurrentRequest.IsCancellationRequested)
- {
- // we don't know what stage the request is cancelled, we can't safely reuse the tcp channel.
- proposedConnectionState = HTTPConnectionStates.Closed;
- this.CurrentRequest.Response = null;
- this.CurrentRequest.State = this.CurrentRequest.IsTimedOut ? HTTPRequestStates.TimedOut : HTTPRequestStates.Aborted;
- }
- else if (resendRequest)
- {
- RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.CurrentRequest, RequestEvents.Resend));
- }
- else if (this.CurrentRequest.Response != null && this.CurrentRequest.Response.IsUpgraded)
- {
- proposedConnectionState = HTTPConnectionStates.WaitForProtocolShutdown;
- }
- else if (this.CurrentRequest.State == HTTPRequestStates.Processing)
- {
- if (this.CurrentRequest.Response != null)
- this.CurrentRequest.State = HTTPRequestStates.Finished;
- else
- {
- this.CurrentRequest.Exception = new Exception(string.Format("[{0}] Remote server closed the connection before sending response header! Previous request state: {1}. Connection state: {2}",
- this.ToString(),
- this.CurrentRequest.State.ToString(),
- this.State.ToString()));
- this.CurrentRequest.State = HTTPRequestStates.Error;
- proposedConnectionState = HTTPConnectionStates.Closed;
- }
- }
- this.CurrentRequest = null;
- if (proposedConnectionState == HTTPConnectionStates.Processing)
- proposedConnectionState = HTTPConnectionStates.Closed;
- ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, proposedConnectionState));
- }
- }
- }
- void OnBuffer(BufferSegment buffer)
- {
- if (Stream != null)
- {
- Stream.Write(buffer);
- //Stream.Write(HTTPRequest.EOL, 0, HTTPRequest.EOL.Length);
- }
- }
- void OnDownloadProgress(int down, int total)
- {
- RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.CurrentRequest, RequestEvents.DownloadProgress, down, total));
- }
- void OnUploadProgress(int up, int total)
- {
- RequestEventHelper.EnqueueRequestEvent(new RequestEventInfo(this.CurrentRequest, RequestEvents.UploadProgress, up, total));
- }
- void OnError(string error)
- {
- HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnError", error, this.Context);
- LastProcessTime = DateTime.UtcNow;
- CurrentRequest.Response = null;
- CurrentRequest.Exception = new Exception(error);
- CurrentRequest.State = HTTPRequestStates.Error;
- ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed));
- }
- void OnTimeout()
- {
- HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnResponse", string.Empty, this.Context);
- CurrentRequest.Response = null;
- CurrentRequest.State = HTTPRequestStates.TimedOut;
- ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed));
- }
- void OnAborted()
- {
- HTTPManager.Logger.Information(this.NativeId + " WebGLConnection - OnAborted", string.Empty, this.Context);
- CurrentRequest.Response = null;
- CurrentRequest.State = HTTPRequestStates.Aborted;
- ConnectionEventHelper.EnqueueConnectionEvent(new ConnectionEventInfo(this, HTTPConnectionStates.Closed));
- }
- protected override void Dispose(bool disposing)
- {
- base.Dispose(disposing);
- if (disposing)
- {
- Connections.Remove(NativeId);
- XHR_Release(NativeId);
- Stream = null;
- }
- }
- #endregion
- #region WebGL Static Callbacks
- [AOT.MonoPInvokeCallback(typeof(OnWebGLRequestHandlerDelegate))]
- static void OnResponse(int nativeId, int httpStatus, IntPtr pBuffer, int length, int err)
- {
- WebGLConnection conn = null;
- if (!Connections.TryGetValue(nativeId, out conn))
- {
- HTTPManager.Logger.Error("WebGLConnection - OnResponse", "No WebGL connection found for nativeId: " + nativeId.ToString());
- return;
- }
- HTTPManager.Logger.Information("WebGLConnection - OnResponse", string.Format("{0} {1} {2} {3}", nativeId, httpStatus, length, err), conn.Context);
- BufferSegment payload = BufferSegment.Empty;
- if (length > 0)
- {
- var buffer = BufferPool.Get(length, true);
- XHR_CopyResponseTo(nativeId, buffer, length);
- payload = new BufferSegment(buffer, 0, length);
- }
- conn.OnResponse(httpStatus, payload);
- }
- [AOT.MonoPInvokeCallback(typeof(OnWebGLBufferDelegate))]
- static void OnBufferCallback(int nativeId, IntPtr pBuffer, int length)
- {
- WebGLConnection conn = null;
- if (!Connections.TryGetValue(nativeId, out conn))
- {
- HTTPManager.Logger.Error("WebGLConnection - OnBufferCallback", "No WebGL connection found for nativeId: " + nativeId.ToString());
- return;
- }
- byte[] buffer = BufferPool.Get(length, true);
- // Copy data from the 'unmanaged' memory to managed memory. Buffer will be reclaimed by the GC.
- Marshal.Copy(pBuffer, buffer, 0, length);
- conn.OnBuffer(new BufferSegment(buffer, 0, length));
- }
- [AOT.MonoPInvokeCallback(typeof(OnWebGLProgressDelegate))]
- static void OnDownloadProgress(int nativeId, int downloaded, int total)
- {
- WebGLConnection conn = null;
- if (!Connections.TryGetValue(nativeId, out conn))
- {
- HTTPManager.Logger.Error("WebGLConnection - OnDownloadProgress", "No WebGL connection found for nativeId: " + nativeId.ToString());
- return;
- }
- HTTPManager.Logger.Information(nativeId + " OnDownloadProgress", downloaded.ToString() + " / " + total.ToString(), conn.Context);
- conn.OnDownloadProgress(downloaded, total);
- }
- [AOT.MonoPInvokeCallback(typeof(OnWebGLProgressDelegate))]
- static void OnUploadProgress(int nativeId, int uploaded, int total)
- {
- WebGLConnection conn = null;
- if (!Connections.TryGetValue(nativeId, out conn))
- {
- HTTPManager.Logger.Error("WebGLConnection - OnUploadProgress", "No WebGL connection found for nativeId: " + nativeId.ToString());
- return;
- }
- HTTPManager.Logger.Information(nativeId + " OnUploadProgress", uploaded.ToString() + " / " + total.ToString(), conn.Context);
- conn.OnUploadProgress(uploaded, total);
- }
- [AOT.MonoPInvokeCallback(typeof(OnWebGLErrorDelegate))]
- static void OnError(int nativeId, string error)
- {
- WebGLConnection conn = null;
- if (!Connections.TryGetValue(nativeId, out conn))
- {
- HTTPManager.Logger.Error("WebGLConnection - OnError", "No WebGL connection found for nativeId: " + nativeId.ToString() + " Error: " + error);
- return;
- }
- conn.OnError(error);
- }
- [AOT.MonoPInvokeCallback(typeof(OnWebGLTimeoutDelegate))]
- static void OnTimeout(int nativeId)
- {
- WebGLConnection conn = null;
- if (!Connections.TryGetValue(nativeId, out conn))
- {
- HTTPManager.Logger.Error("WebGLConnection - OnTimeout", "No WebGL connection found for nativeId: " + nativeId.ToString());
- return;
- }
- conn.OnTimeout();
- }
- [AOT.MonoPInvokeCallback(typeof(OnWebGLAbortedDelegate))]
- static void OnAborted(int nativeId)
- {
- WebGLConnection conn = null;
- if (!Connections.TryGetValue(nativeId, out conn))
- {
- HTTPManager.Logger.Error("WebGLConnection - OnAborted", "No WebGL connection found for nativeId: " + nativeId.ToString());
- return;
- }
- conn.OnAborted();
- }
- #endregion
- #region WebGL Interface
- [DllImport("__Internal")]
- private static extern int XHR_Create(string method, string url, string userName, string passwd, int withCredentials);
- /// <summary>
- /// 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.
- /// </summary>
- [DllImport("__Internal")]
- private static extern void XHR_SetTimeout(int nativeId, uint timeout);
- [DllImport("__Internal")]
- private static extern void XHR_SetRequestHeader(int nativeId, string header, string value);
- [DllImport("__Internal")]
- private static extern void XHR_SetResponseHandler(int nativeId, OnWebGLRequestHandlerDelegate onresponse, OnWebGLErrorDelegate onerror, OnWebGLTimeoutDelegate ontimeout, OnWebGLAbortedDelegate onabort);
- [DllImport("__Internal")]
- private static extern void XHR_SetProgressHandler(int nativeId, OnWebGLProgressDelegate onDownloadProgress, OnWebGLProgressDelegate onUploadProgress);
- [DllImport("__Internal")]
- private static extern void XHR_CopyResponseTo(int nativeId, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] response, int length);
- [DllImport("__Internal")]
- private static extern void XHR_Send(int nativeId, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] body, int length);
- [DllImport("__Internal")]
- private static extern void XHR_GetResponseHeaders(int nativeId, OnWebGLBufferDelegate callback);
- [DllImport("__Internal")]
- private static extern void XHR_GetStatusLine(int nativeId, OnWebGLBufferDelegate callback);
- [DllImport("__Internal")]
- private static extern void XHR_Abort(int nativeId);
- [DllImport("__Internal")]
- private static extern void XHR_Release(int nativeId);
- [DllImport("__Internal")]
- private static extern void XHR_SetLoglevel(int logLevel);
- #endregion
- }
- }
- #endif
|