diff --git a/Documentation~/README.md b/Documentation~/README.md
index 30c1854..c18ce97 100644
--- a/Documentation~/README.md
+++ b/Documentation~/README.md
@@ -74,6 +74,9 @@ socket.OnClose += (code, reason) => Debug.Log($"Connection Closed: {code} {reaso
socket.Connect();
```
+> [!NOTE]
+> `socket.ConnectAsync()` is blocking until the connection is closed.
+
### Handling Events
You can subscribe to the `OnOpen`, `OnMessage`, `OnError`, and `OnClose` events to handle respective situations:
diff --git a/Runtime/IWebSocket.cs b/Runtime/IWebSocket.cs
index ccb7dd3..e5bb730 100644
--- a/Runtime/IWebSocket.cs
+++ b/Runtime/IWebSocket.cs
@@ -34,6 +34,11 @@ public interface IWebSocket : IDisposable
///
Uri Address { get; }
+ ///
+ /// The request headers used by the .
+ ///
+ IReadOnlyDictionary RequestHeaders { get; }
+
///
/// The sub-protocols used by the .
///
diff --git a/Runtime/Plugins/WebSocket.jslib b/Runtime/Plugins/WebSocket.jslib
index d1d39ce..d145301 100644
--- a/Runtime/Plugins/WebSocket.jslib
+++ b/Runtime/Plugins/WebSocket.jslib
@@ -10,7 +10,7 @@ var UnityWebSocketLibrary = {
/**
* Create a new WebSocket instance and adds it to the $webSockets array.
* @param {string} url - The URL to which to connect.
- * @param {string[]} subProtocols - An array of strings that indicate the sub-protocols the client is willing to speak.
+ * @param {string[]} subProtocols - An json array of strings that indicate the sub-protocols the client is willing to speak.
* @returns {number} - A pointer to the WebSocket instance.
* @param {function} onOpenCallback - The callback function. WebSocket_OnOpenDelegate(IntPtr websocketPtr) in C#.
* @param {function} onMessageCallback - The callback function. WebSocket_OnMessageDelegate(IntPtr websocketPtr, IntPtr data, int length, int type) in C#.
@@ -22,7 +22,7 @@ var UnityWebSocketLibrary = {
try {
var subProtocolsStr = UTF8ToString(subProtocols);
- var subProtocolsArr = subProtocolsStr ? subProtocolsStr.split(',') : undefined;
+ var subProtocolsArr = subProtocolsStr ? JSON.parse(subProtocolsStr) : undefined;
for (var i = 0; i < webSockets.length; i++) {
var instance = webSockets[i];
@@ -43,11 +43,13 @@ var UnityWebSocketLibrary = {
onCloseCallback: onCloseCallback
};
- if (subProtocolsArr) {
+ if (subProtocolsArr && Array.isArray(subProtocolsArr)) {
webSockets[socketPtr].subProtocols = subProtocolsArr;
+ } else {
+ console.error('subProtocols is not an array');
}
- // console.log('Created WebSocket object with websocketPtr: ', socketPtr, ' for URL: ', urlStr, ' and sub-protocols: ', subProtocolsArr)
+ // console.log(`Created WebSocket object with websocketPtr: ${socketPtr} for URL: ${urlStr}, sub-protocols: ${subProtocolsArr}`);
return socketPtr;
} catch (error) {
console.error('Error creating WebSocket object for URL: ', urlStr, ' Error: ', error);
@@ -81,6 +83,11 @@ var UnityWebSocketLibrary = {
try {
var instance = webSockets[socketPtr];
+ if (!instance) {
+ console.error('WebSocket instance not found for websocketPtr: ', socketPtr);
+ return;
+ }
+
if (!instance.subProtocols || instance.subProtocols.length === 0) {
instance.socket = new WebSocket(instance.url);
} else {
diff --git a/Runtime/WebSocket.cs b/Runtime/WebSocket.cs
index c294701..f3d37fd 100644
--- a/Runtime/WebSocket.cs
+++ b/Runtime/WebSocket.cs
@@ -17,12 +17,12 @@ namespace Utilities.WebSockets
{
public class WebSocket : IWebSocket
{
- public WebSocket(string url, IReadOnlyList subProtocols = null)
- : this(new Uri(url), subProtocols)
+ public WebSocket(string url, IReadOnlyDictionary requestHeaders = null, IReadOnlyList subProtocols = null)
+ : this(new Uri(url), requestHeaders, subProtocols)
{
}
- public WebSocket(Uri uri, IReadOnlyList subProtocols = null)
+ public WebSocket(Uri uri, IReadOnlyDictionary requestHeaders = null, IReadOnlyList subProtocols = null)
{
var protocol = uri.Scheme;
@@ -32,6 +32,7 @@ public WebSocket(Uri uri, IReadOnlyList subProtocols = null)
}
Address = uri;
+ RequestHeaders = requestHeaders ?? new Dictionary();
SubProtocols = subProtocols ?? new List();
_socket = new ClientWebSocket();
RunMessageQueue();
@@ -59,10 +60,7 @@ private async void RunMessageQueue()
}
}
- ~WebSocket()
- {
- Dispose(false);
- }
+ ~WebSocket() => Dispose(false);
#region IDisposable
@@ -114,6 +112,9 @@ public void Dispose()
///
public Uri Address { get; }
+ ///
+ public IReadOnlyDictionary RequestHeaders { get; }
+
///
public IReadOnlyList SubProtocols { get; }
@@ -126,7 +127,7 @@ public void Dispose()
_ => State.Closed
};
- private object _lock = new();
+ private readonly object _lock = new();
private ClientWebSocket _socket;
private SemaphoreSlim _semaphore = new(1, 1);
private CancellationTokenSource _lifetimeCts;
@@ -151,13 +152,19 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
_lifetimeCts?.Dispose();
_lifetimeCts = new CancellationTokenSource();
using var cts = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeCts.Token, cancellationToken);
+ cancellationToken = cts.Token;
+
+ foreach (var requestHeader in RequestHeaders)
+ {
+ _socket.Options.SetRequestHeader(requestHeader.Key, requestHeader.Value);
+ }
foreach (var subProtocol in SubProtocols)
{
_socket.Options.AddSubProtocol(subProtocol);
}
- await _socket.ConnectAsync(Address, cts.Token).ConfigureAwait(false);
+ await _socket.ConnectAsync(Address, cancellationToken).ConfigureAwait(false);
_events.Enqueue(() => OnOpen?.Invoke());
var buffer = new Memory(new byte[8192]);
@@ -168,11 +175,12 @@ public async Task ConnectAsync(CancellationToken cancellationToken = default)
do
{
- result = await _socket.ReceiveAsync(buffer, cts.Token).ConfigureAwait(false);
+ cancellationToken.ThrowIfCancellationRequested();
+ result = await _socket.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false);
stream.Write(buffer.Span[..result.Count]);
} while (!result.EndOfMessage);
- await stream.FlushAsync(cts.Token).ConfigureAwait(false);
+ await stream.FlushAsync(cancellationToken).ConfigureAwait(false);
var memory = new ReadOnlyMemory(stream.GetBuffer(), 0, (int)stream.Length);
if (result.MessageType != WebSocketMessageType.Close)
diff --git a/Runtime/WebSocket_WebGL.cs b/Runtime/WebSocket_WebGL.cs
index 18e3717..c6b7edb 100644
--- a/Runtime/WebSocket_WebGL.cs
+++ b/Runtime/WebSocket_WebGL.cs
@@ -11,17 +11,18 @@
using System.Threading.Tasks;
using UnityEngine;
using Utilities.Async;
+using Newtonsoft.Json;
namespace Utilities.WebSockets
{
public class WebSocket : IWebSocket
{
- public WebSocket(string url, IReadOnlyList subProtocols = null)
- : this(new Uri(url), subProtocols)
+ public WebSocket(string url, IReadOnlyDictionary requestHeaders = null, IReadOnlyList subProtocols = null)
+ : this(new Uri(url), requestHeaders, subProtocols)
{
}
- public WebSocket(Uri uri, IReadOnlyList subProtocols = null)
+ public WebSocket(Uri uri, IReadOnlyDictionary requestHeaders = null, IReadOnlyList subProtocols = null)
{
var protocol = uri.Scheme;
@@ -30,9 +31,21 @@ public WebSocket(Uri uri, IReadOnlyList subProtocols = null)
throw new ArgumentException($"Unsupported protocol: {protocol}");
}
+ if (requestHeaders is { Count: > 0 })
+ {
+ Debug.LogWarning("Request Headers are not supported in WebGL and will be ignored.");
+ }
+
Address = uri;
SubProtocols = subProtocols ?? new List();
- _socket = WebSocket_Create(uri.ToString(), string.Join(',', SubProtocols), WebSocket_OnOpen, WebSocket_OnMessage, WebSocket_OnError, WebSocket_OnClose);
+ RequestHeaders = requestHeaders ?? new Dictionary();
+ _socket = WebSocket_Create(
+ uri.ToString(),
+ JsonConvert.SerializeObject(subProtocols),
+ WebSocket_OnOpen,
+ WebSocket_OnMessage,
+ WebSocket_OnError,
+ WebSocket_OnClose);
if (_socket == IntPtr.Zero || !_sockets.TryAdd(_socket, this))
{
@@ -210,6 +223,8 @@ private static void WebSocket_OnClose(IntPtr websocketPtr, CloseStatusCode code,
///
public Uri Address { get; }
+ public IReadOnlyDictionary RequestHeaders { get; }
+
///
public IReadOnlyList SubProtocols { get; }
@@ -218,7 +233,7 @@ private static void WebSocket_OnClose(IntPtr websocketPtr, CloseStatusCode code,
? (State)WebSocket_GetState(_socket)
: State.Closed;
- private object _lock = new();
+ private readonly object _lock = new();
private IntPtr _socket;
private SemaphoreSlim _semaphore = new(1, 1);
private CancellationTokenSource _lifetimeCts;
diff --git a/package.json b/package.json
index 04408ad..5f22bd6 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"displayName": "Utilities.WebSockets",
"description": "A simple websocket package for Unity (UPM)",
"keywords": [],
- "version": "1.0.0-preview.2",
+ "version": "1.0.0",
"unity": "2021.3",
"documentationUrl": "https://github.com/RageAgainstThePixel/com.utilities.websockets#documentation",
"changelogUrl": "https://github.com/RageAgainstThePixel/com.utilities.websockets/releases",
@@ -17,7 +17,8 @@
"url": "https://github.com/StephenHodgson"
},
"dependencies": {
- "com.utilities.async": "2.1.7"
+ "com.utilities.async": "2.1.7",
+ "com.unity.nuget.newtonsoft-json": "3.2.1"
},
"samples": [
{