284 lines
10 KiB
C#
284 lines
10 KiB
C#
#if UNITY_WEBGL && !UNITY_EDITOR
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Runtime.InteropServices;
|
|
|
|
using Best.HTTP.Shared;
|
|
using Best.HTTP.Shared.PlatformSupport.Memory;
|
|
|
|
namespace Best.WebSockets.Implementations
|
|
{
|
|
delegate void OnWebGLWebSocketOpenDelegate(uint id);
|
|
delegate void OnWebGLWebSocketTextDelegate(uint id, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] textBuffer, int allocatedLength, int length);
|
|
delegate void OnWebGLWebSocketBinaryDelegate(uint id, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] buffer, int allocatedLength, int length);
|
|
delegate void OnWebGLWebSocketErrorDelegate(uint id, string error);
|
|
delegate void OnWebGLWebSocketCloseDelegate(uint id, int code, string reason);
|
|
delegate IntPtr OnWebGLAllocArray(int nativeId, int size);
|
|
internal sealed class WebGLBrowser : WebSocketBaseImplementation
|
|
{
|
|
public override WebSocketStates State => ImplementationId != 0 ? WS_GetState(ImplementationId) : WebSocketStates.Unknown;
|
|
|
|
public override bool IsOpen => ImplementationId != 0 && WS_GetState(ImplementationId) == WebSocketStates.Open;
|
|
public override int BufferedAmount => WS_GetBufferedAmount(ImplementationId);
|
|
|
|
internal static Dictionary<uint, WebSocket> WebSockets = new Dictionary<uint, WebSocket>();
|
|
|
|
private uint ImplementationId;
|
|
|
|
public WebGLBrowser(WebSocket parent, Uri uri, string origin, string protocol) : base(parent, uri, origin, protocol)
|
|
{
|
|
}
|
|
|
|
public override void StartOpen()
|
|
{
|
|
try
|
|
{
|
|
ImplementationId = WS_Create(this.Uri.OriginalString, this.Protocol, OnOpenCallback, OnTextCallback, OnBinaryCallback, OnErrorCallback, OnCloseCallback, Allocator);
|
|
WebSockets.Add(ImplementationId, this.Parent);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
HTTPManager.Logger.Exception("WebSocket", "Open", ex, this.Parent.Context);
|
|
}
|
|
}
|
|
|
|
public override void StartClose(WebSocketStatusCodes code, string message)
|
|
{
|
|
WS_Close(this.ImplementationId, (ushort)code, message);
|
|
}
|
|
|
|
public override void Send(string message)
|
|
{
|
|
var count = System.Text.Encoding.UTF8.GetByteCount(message);
|
|
var buffer = BufferPool.Get(count, true);
|
|
|
|
System.Text.Encoding.UTF8.GetBytes(message, 0, message.Length, buffer, 0);
|
|
|
|
WS_Send_String(this.ImplementationId, buffer, 0, count);
|
|
|
|
BufferPool.Release(buffer);
|
|
}
|
|
|
|
public override void Send(byte[] buffer)
|
|
{
|
|
WS_Send_Binary(this.ImplementationId, buffer, 0, buffer.Length);
|
|
}
|
|
|
|
public override void Send(byte[] buffer, ulong offset, ulong count)
|
|
{
|
|
WS_Send_Binary(this.ImplementationId, buffer, (int)offset, (int)count);
|
|
}
|
|
|
|
public override void SendAsBinary(BufferSegment data)
|
|
{
|
|
WS_Send_Binary(this.ImplementationId, data.Data, data.Offset, data.Count);
|
|
BufferPool.Release(data);
|
|
}
|
|
|
|
public override void SendAsText(BufferSegment data)
|
|
{
|
|
WS_Send_String(this.ImplementationId, data.Data, data.Offset, data.Count);
|
|
BufferPool.Release(data);
|
|
}
|
|
|
|
[DllImport("__Internal")]
|
|
static extern uint WS_Create(string url,
|
|
string protocol,
|
|
OnWebGLWebSocketOpenDelegate onOpen,
|
|
OnWebGLWebSocketTextDelegate onText,
|
|
OnWebGLWebSocketBinaryDelegate onBinary,
|
|
OnWebGLWebSocketErrorDelegate onError,
|
|
OnWebGLWebSocketCloseDelegate onClose,
|
|
OnWebGLAllocArray allocator);
|
|
|
|
[DllImport("__Internal")]
|
|
static extern WebSocketStates WS_GetState(uint id);
|
|
|
|
[DllImport("__Internal")]
|
|
static extern int WS_GetBufferedAmount(uint id);
|
|
|
|
[DllImport("__Internal")]
|
|
static extern int WS_Send_String(uint id, byte[] strData, int pos, int length);
|
|
|
|
[DllImport("__Internal")]
|
|
static extern int WS_Send_Binary(uint id, byte[] buffer, int pos, int length);
|
|
|
|
[DllImport("__Internal")]
|
|
static extern void WS_Close(uint id, ushort code, string reason);
|
|
|
|
[DllImport("__Internal")]
|
|
static extern void WS_Release(uint id);
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(OnWebGLAllocArray))]
|
|
static unsafe IntPtr Allocator(int nativeId, int length)
|
|
{
|
|
byte[] buffer = BufferPool.Get(length, true);
|
|
|
|
if (HTTPManager.Logger.IsDiagnostic)
|
|
HTTPManager.Logger.Verbose(nameof(WebGLBrowser), $"Allocator - allocated: {buffer.Length}");
|
|
|
|
buffer[0] = (byte)(buffer.Length >> 24);
|
|
buffer[1] = (byte)(buffer.Length >> 16);
|
|
buffer[2] = (byte)(buffer.Length >> 8);
|
|
buffer[3] = (byte)(buffer.Length);
|
|
|
|
fixed (byte* ptr = buffer)
|
|
{
|
|
var p = (IntPtr)ptr;
|
|
|
|
if (HTTPManager.Logger.IsDiagnostic)
|
|
HTTPManager.Logger.Verbose(nameof(WebGLBrowser), $"({p}) <= Allocator({nativeId}, {length})");
|
|
|
|
return p;
|
|
}
|
|
}
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(OnWebGLWebSocketOpenDelegate))]
|
|
static void OnOpenCallback(uint id)
|
|
{
|
|
WebSocket ws;
|
|
if (WebSockets.TryGetValue(id, out ws))
|
|
{
|
|
if (ws.OnOpen != null)
|
|
{
|
|
try
|
|
{
|
|
ws.OnOpen(ws);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
HTTPManager.Logger.Exception("WebSocket", "OnOpen", ex, ws.Context);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
HTTPManager.Logger.Warning("WebSocket", "OnOpenCallback - No WebSocket found for id: " + id.ToString(), ws.Context);
|
|
}
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(OnWebGLWebSocketTextDelegate))]
|
|
static void OnTextCallback(uint id, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] textBuffer, int allocatedLength, int length)
|
|
{
|
|
try
|
|
{
|
|
WebSocket ws;
|
|
if (WebSockets.TryGetValue(id, out ws))
|
|
{
|
|
if (ws.OnMessage != null)
|
|
{
|
|
try
|
|
{
|
|
var text = System.Text.Encoding.UTF8.GetString(textBuffer, 0, length);
|
|
|
|
if (HTTPManager.Logger.IsDiagnostic)
|
|
HTTPManager.Logger.Verbose(nameof(WebGLBrowser), $"{id}, {textBuffer}, {length} => {text}", ws.Context);
|
|
ws.OnMessage(ws, text);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
HTTPManager.Logger.Exception("WebSocket", "OnMessage", ex, ws.Context);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
HTTPManager.Logger.Warning("WebSocket", "OnTextCallback - No WebSocket found for id: " + id.ToString());
|
|
}
|
|
finally
|
|
{
|
|
BufferPool.Release(textBuffer);
|
|
}
|
|
}
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(OnWebGLWebSocketBinaryDelegate))]
|
|
static void OnBinaryCallback(uint id, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 2)] byte[] buffer, int allocatedLength, int length)
|
|
{
|
|
WebSocket ws;
|
|
if (WebSockets.TryGetValue(id, out ws))
|
|
{
|
|
if (ws.OnBinary != null)
|
|
{
|
|
try
|
|
{
|
|
ws.OnBinary(ws, new BufferSegment(buffer, 0, length));
|
|
|
|
BufferPool.Release(buffer);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
HTTPManager.Logger.Exception("WebSocket", "OnBinary", ex, ws.Context);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
HTTPManager.Logger.Warning("WebSocket", "OnBinaryCallback - No WebSocket found for id: " + id.ToString());
|
|
}
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(OnWebGLWebSocketErrorDelegate))]
|
|
static void OnErrorCallback(uint id, string error)
|
|
{
|
|
WebSocket ws;
|
|
if (WebSockets.TryGetValue(id, out ws))
|
|
{
|
|
WebSockets.Remove(id);
|
|
|
|
if (ws.OnClosed != null)
|
|
{
|
|
try
|
|
{
|
|
ws.OnClosed(ws, WebSocketStatusCodes.ClosedAbnormally, error);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
HTTPManager.Logger.Exception("WebSocket", "OnError", ex, ws.Context);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
HTTPManager.Logger.Warning("WebSocket", "OnErrorCallback - No WebSocket found for id: " + id.ToString());
|
|
|
|
try
|
|
{
|
|
WS_Release(id);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
HTTPManager.Logger.Exception("WebSocket", "WS_Release", ex);
|
|
}
|
|
}
|
|
|
|
[AOT.MonoPInvokeCallback(typeof(OnWebGLWebSocketCloseDelegate))]
|
|
static void OnCloseCallback(uint id, int code, string reason)
|
|
{
|
|
WebSocket ws;
|
|
if (WebSockets.TryGetValue(id, out ws))
|
|
{
|
|
WebSockets.Remove(id);
|
|
|
|
if (ws.OnClosed != null)
|
|
{
|
|
try
|
|
{
|
|
ws.OnClosed(ws, (WebSocketStatusCodes)code, reason);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
HTTPManager.Logger.Exception("WebSocket", "OnClosed", ex, ws.Context);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
HTTPManager.Logger.Warning("WebSocket", "OnCloseCallback - No WebSocket found for id: " + id.ToString());
|
|
|
|
try
|
|
{
|
|
WS_Release(id);
|
|
}
|
|
catch(Exception ex)
|
|
{
|
|
HTTPManager.Logger.Exception("WebSocket", "WS_Release", ex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|