File: src\sdk\src\Dotnet.Watch\HotReloadClient\Web\BrowserConnection.cs
Web Access
Project: Microsoft.DotNet.HotReload.Watch.csproj (Microsoft.DotNet.HotReload.Watch)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable enable

using System;
using System.Buffers;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace Microsoft.DotNet.HotReload;

/// <summary>
/// Represents a connection to a browser that facilitates Hot Reload operations.
/// </summary>
internal readonly struct BrowserConnection(WebSocket clientSocket, string? sharedSecret, int id, ILogger serverLogger, ILogger agentLogger) : IDisposable
{
    public readonly TaskCompletionSource<None> Disconnected = new(TaskCreationOptions.RunContinuationsAsynchronously);

    public void Dispose()
    {
        ClientSocket.Dispose();

        Disconnected.TrySetResult(default);
        ServerLogger.LogDebug("Disconnected.");
    }

    public WebSocket ClientSocket => clientSocket;
    public string? SharedSecret => sharedSecret;
    public int Id => id;
    public ILogger ServerLogger => serverLogger;
    public ILogger AgentLogger => agentLogger;

    internal async ValueTask<bool> TrySendMessageAsync(ReadOnlyMemory<byte> messageBytes, CancellationToken cancellationToken)
    {
#if NET
        var data = messageBytes;
#else
        var data = new ArraySegment<byte>(messageBytes.ToArray());
#endif
        try
        {
            await ClientSocket.SendAsync(data, WebSocketMessageType.Text, endOfMessage: true, cancellationToken);
        }
        catch (Exception e) when (e is not OperationCanceledException)
        {
            ServerLogger.LogDebug("Failed to send message: {Message}", e.Message);
            return false;
        }

        return true;
    }

    internal async ValueTask<TResponseResult?> TryReceiveMessageAsync<TResponseResult>(ResponseFunc<TResponseResult> receiver, CancellationToken cancellationToken)
        where TResponseResult : struct
    {
        var writer = new ArrayBufferWriter<byte>(initialCapacity: 1024);

        while (true)
        {
#if NET
            ValueWebSocketReceiveResult result;
            var data = writer.GetMemory();
#else
            WebSocketReceiveResult result;
            var data = writer.GetArraySegment();
#endif
            try
            {
                result = await ClientSocket.ReceiveAsync(data, cancellationToken);
            }
            catch (Exception e) when (e is not OperationCanceledException)
            {
                ServerLogger.LogDebug("Failed to receive response: {Message}", e.Message);
                return null;
            }

            if (result.MessageType == WebSocketMessageType.Close)
            {
                return null;
            }

            writer.Advance(result.Count);
            if (result.EndOfMessage)
            {
                break;
            }
        }

        return receiver(writer.WrittenSpan, AgentLogger);
    }
}