aspnetcore/test/Microsoft.AspNetCore.Socket.../TestWebSocketConnectionFeat...

188 lines
7.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Net.WebSockets;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Channels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Microsoft.AspNetCore.Sockets.Tests
{
internal class TestWebSocketConnectionFeature : IHttpWebSocketFeature, IDisposable
{
public bool IsWebSocketRequest => true;
public WebSocketChannel Client { get; private set; }
public Task<WebSocket> AcceptAsync() => AcceptAsync(new WebSocketAcceptContext());
public Task<WebSocket> AcceptAsync(WebSocketAcceptContext context)
{
var clientToServer = Channel.CreateUnbounded<WebSocketMessage>();
var serverToClient = Channel.CreateUnbounded<WebSocketMessage>();
var clientSocket = new WebSocketChannel(serverToClient.In, clientToServer.Out);
var serverSocket = new WebSocketChannel(clientToServer.In, serverToClient.Out);
Client = clientSocket;
return Task.FromResult<WebSocket>(serverSocket);
}
public void Dispose()
{
}
public class WebSocketChannel : WebSocket
{
private readonly ReadableChannel<WebSocketMessage> _input;
private readonly WritableChannel<WebSocketMessage> _output;
private WebSocketCloseStatus? _closeStatus;
private string _closeStatusDescription;
private WebSocketState _state;
public WebSocketChannel(ReadableChannel<WebSocketMessage> input, WritableChannel<WebSocketMessage> output)
{
_input = input;
_output = output;
}
public override WebSocketCloseStatus? CloseStatus => _closeStatus;
public override string CloseStatusDescription => _closeStatusDescription;
public override WebSocketState State => _state;
public override string SubProtocol => null;
public override void Abort()
{
_output.TryComplete(new OperationCanceledException());
_state = WebSocketState.Aborted;
}
public override async Task CloseAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
{
await SendMessageAsync(new WebSocketMessage
{
CloseStatus = closeStatus,
CloseStatusDescription = statusDescription,
MessageType = WebSocketMessageType.Close,
},
cancellationToken);
_state = WebSocketState.CloseSent;
_output.TryComplete();
}
public override async Task CloseOutputAsync(WebSocketCloseStatus closeStatus, string statusDescription, CancellationToken cancellationToken)
{
await SendMessageAsync(new WebSocketMessage
{
CloseStatus = closeStatus,
CloseStatusDescription = statusDescription,
MessageType = WebSocketMessageType.Close,
},
cancellationToken);
_state = WebSocketState.CloseSent;
_output.TryComplete();
}
public override void Dispose()
{
_state = WebSocketState.Closed;
_output.TryComplete();
}
public override async Task<WebSocketReceiveResult> ReceiveAsync(ArraySegment<byte> buffer, CancellationToken cancellationToken)
{
var message = await _input.ReadAsync();
if (message.MessageType == WebSocketMessageType.Close)
{
_state = WebSocketState.CloseReceived;
_closeStatus = message.CloseStatus;
_closeStatusDescription = message.CloseStatusDescription;
return new WebSocketReceiveResult(0, WebSocketMessageType.Close, true, message.CloseStatus, message.CloseStatusDescription);
}
// REVIEW: This assumes the buffer passed in is > the buffer received
Buffer.BlockCopy(message.Buffer, 0, buffer.Array, buffer.Offset, message.Buffer.Length);
return new WebSocketReceiveResult(message.Buffer.Length, message.MessageType, message.EndOfMessage);
}
public override Task SendAsync(ArraySegment<byte> buffer, WebSocketMessageType messageType, bool endOfMessage, CancellationToken cancellationToken)
{
var copy = new byte[buffer.Count];
Buffer.BlockCopy(buffer.Array, buffer.Offset, copy, 0, buffer.Count);
return SendMessageAsync(new WebSocketMessage
{
Buffer = copy,
MessageType = messageType,
EndOfMessage = endOfMessage
},
cancellationToken);
}
public async Task<WebSocketConnectionSummary> ExecuteAndCaptureFramesAsync()
{
var frames = new List<WebSocketMessage>();
while (await _input.WaitToReadAsync())
{
while (_input.TryRead(out var message))
{
if (message.MessageType == WebSocketMessageType.Close)
{
_state = WebSocketState.CloseReceived;
_closeStatus = message.CloseStatus;
_closeStatusDescription = message.CloseStatusDescription;
return new WebSocketConnectionSummary(frames, new WebSocketReceiveResult(0, message.MessageType, message.EndOfMessage, message.CloseStatus, message.CloseStatusDescription));
}
frames.Add(message);
}
}
_state = WebSocketState.Closed;
_closeStatus = WebSocketCloseStatus.InternalServerError;
return new WebSocketConnectionSummary(frames, new WebSocketReceiveResult(0, WebSocketMessageType.Close, endOfMessage: true, closeStatus: WebSocketCloseStatus.InternalServerError, closeStatusDescription: ""));
}
private async Task SendMessageAsync(WebSocketMessage webSocketMessage, CancellationToken cancellationToken)
{
while (await _output.WaitToWriteAsync(cancellationToken))
{
if (_output.TryWrite(webSocketMessage))
{
break;
}
}
}
}
public class WebSocketConnectionSummary
{
public IList<WebSocketMessage> Received { get; }
public WebSocketReceiveResult CloseResult { get; }
public WebSocketConnectionSummary(IList<WebSocketMessage> received, WebSocketReceiveResult closeResult)
{
Received = received;
CloseResult = closeResult;
}
}
public class WebSocketMessage
{
public byte[] Buffer { get; set; }
public WebSocketMessageType MessageType { get; set; }
public bool EndOfMessage { get; set; }
public WebSocketCloseStatus? CloseStatus { get; set; }
public string CloseStatusDescription { get; set; }
}
}
}