Skip to content

Commit

Permalink
[Re:#167](supabase-community/supabase-csharp#167) Adds support for sp…
Browse files Browse the repository at this point in the history
…ecifying `GetHeaders` on the `RealtimeClient` which are included on the initial request to the server to establish websocket connection.
  • Loading branch information
acupofjose committed Jul 26, 2024
1 parent 04e1821 commit b81fc39
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 10 deletions.
24 changes: 20 additions & 4 deletions Realtime/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,21 @@ public class Client : IRealtimeClient<RealtimeSocket, RealtimeChannel>
/// </summary>
public ClientOptions Options { get; }

private Func<Dictionary<string, string>>? _getHeaders { get; set; }

/// <inheritdoc />
public Func<Dictionary<string, string>>? GetHeaders
{
get => _getHeaders;
set
{
_getHeaders = value;

if (Socket != null)
Socket.GetHeaders = value;
}
}

/// <summary>
/// Custom Serializer resolvers and converters that will be used for encoding and decoding Postgrest JSON responses.
///
Expand Down Expand Up @@ -124,6 +139,7 @@ public async Task<IRealtimeClient<RealtimeSocket, RealtimeChannel>> ConnectAsync
}

Socket = new RealtimeSocket(_realtimeUrl, Options);
Socket.GetHeaders = GetHeaders;

IRealtimeSocket.StateEventHandler? socketStateHandler = null;
socketStateHandler = (sender, state) =>
Expand Down Expand Up @@ -172,7 +188,7 @@ public IRealtimeClient<RealtimeSocket, RealtimeChannel> Connect(
callback?.Invoke(this, null);
return this;
}

Socket = new RealtimeSocket(_realtimeUrl, Options);
IRealtimeSocket.StateEventHandler? socketStateHandler = null;
IRealtimeSocket.ErrorEventHandler? errorEventHandler = null;
Expand All @@ -183,12 +199,12 @@ public IRealtimeClient<RealtimeSocket, RealtimeChannel> Connect(

Socket.AddMessageReceivedHandler(HandleSocketMessageReceived);
Socket.AddHeartbeatHandler(HandleSocketHeartbeat);

sender.RemoveStateChangedHandler(socketStateHandler!);
sender.RemoveErrorHandler(errorEventHandler!);

NotifySocketStateChange(SocketState.Open);

callback?.Invoke(this, null);
};

Expand Down
2 changes: 1 addition & 1 deletion Realtime/ClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public class ClientOptions
/// <summary>
/// Request headers to be appended to the connection string.
/// </summary>
public readonly Dictionary<string, object> Headers = new();
public readonly Dictionary<string, string> Headers = new();

/// <summary>
/// The optional params to pass when connecting
Expand Down
3 changes: 2 additions & 1 deletion Realtime/Interfaces/IRealtimeClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.ObjectModel;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Supabase.Core.Interfaces;
using Supabase.Realtime.Exceptions;
using static Supabase.Realtime.Constants;

Expand All @@ -14,7 +15,7 @@ namespace Supabase.Realtime.Interfaces;
/// </summary>
/// <typeparam name="TSocket"></typeparam>
/// <typeparam name="TChannel"></typeparam>
public interface IRealtimeClient<TSocket, TChannel>
public interface IRealtimeClient<TSocket, TChannel>: IGettableHeaders
where TSocket : IRealtimeSocket
where TChannel : IRealtimeChannel
{
Expand Down
3 changes: 2 additions & 1 deletion Realtime/Interfaces/IRealtimeSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Net.WebSockets;
using System.Threading.Tasks;
using Supabase.Core.Interfaces;
using Supabase.Realtime.Exceptions;
using static Supabase.Realtime.Constants;

Expand All @@ -10,7 +11,7 @@ namespace Supabase.Realtime.Interfaces;
/// <summary>
/// Contract for a realtime socket.
/// </summary>
public interface IRealtimeSocket
public interface IRealtimeSocket: IGettableHeaders
{
/// <summary>
/// Is this socket connected?
Expand Down
6 changes: 5 additions & 1 deletion Realtime/Realtime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,14 @@
<Version Condition=" '$(Version)' == '' ">$(VersionPrefix)</Version>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="RealtimeTests" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Supabase.Core" Version="1.0.0" />
<PackageReference Include="Supabase.Postgrest" Version="4.0.0" />
<PackageReference Include="Supabase.Postgrest" Version="4.0.3" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
Expand Down
21 changes: 19 additions & 2 deletions Realtime/RealtimeSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Supabase.Core.Extensions;
using Supabase.Realtime.Socket;
using Supabase.Realtime.Exceptions;
using Supabase.Realtime.Interfaces;
Expand All @@ -15,7 +16,6 @@
#endif

namespace Supabase.Realtime;

/// <summary>
/// Socket connection handler.
/// </summary>
Expand Down Expand Up @@ -44,6 +44,15 @@ private string EndpointUrl
}
}

/// <inheritdoc />
public Func<Dictionary<string, string>>? GetHeaders { get; set; }

/// <summary>
/// Shortcut property that merges <see cref="GetHeaders"/> with <see cref="_options"/>
/// Headers specified in <see cref="_options"/> take precedence over <see cref="GetHeaders"/>
/// </summary>
internal Dictionary<string, string> Headers => GetHeaders != null ? GetHeaders().MergeLeft(_options.Headers) : _options.Headers;

private readonly List<IRealtimeSocket.StateEventHandler> _socketEventHandlers = new();
private readonly List<IRealtimeSocket.MessageEventHandler> _messageEventHandlers = new();
private readonly List<IRealtimeSocket.HeartbeatEventHandler> _heartbeatEventHandlers = new();
Expand Down Expand Up @@ -76,7 +85,15 @@ public RealtimeSocket(string endpoint, ClientOptions options)
if (!options.Headers.ContainsKey("X-Client-Info"))
options.Headers.Add("X-Client-Info", Core.Util.GetAssemblyVersion(typeof(Client)));

_connection = new WebsocketClient(new Uri(EndpointUrl));
_connection = new WebsocketClient(new Uri(EndpointUrl), () =>
{
var socket = new ClientWebSocket();

foreach (var header in Headers)
socket.Options.SetRequestHeader(header.Key, header.Value);

return socket;
});
}

void IDisposable.Dispose()
Expand Down
16 changes: 16 additions & 0 deletions RealtimeTests/ClientTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Supabase.Realtime.Exceptions;
Expand Down Expand Up @@ -130,4 +132,18 @@ public async Task ClientCanReconnectAfterProgrammaticDisconnect()
client!.Disconnect();
await client!.ConnectAsync();
}

[TestMethod("Client: Sets headers")]
public async Task ClientCanSetHeaders()
{
client!.Disconnect();

client!.GetHeaders = () => new Dictionary<string, string>() { { "testing", "123" } };
await client.ConnectAsync();

Assert.IsNotNull(client!);
Assert.IsNotNull(client!.Socket);
Assert.IsNotNull(client!.Socket.GetHeaders);
Assert.AreEqual("123",client.Socket.GetHeaders()["testing"]);
}
}

0 comments on commit b81fc39

Please sign in to comment.