< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 179
Coverable lines: 179
Total lines: 385
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 74
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%110%
Create(...)0%220%
Create(...)0%440%
CreateWritableMessageStream(...)100%110%
CreateReadableMessageStream(...)100%110%
Dispose(...)0%220%
Flush()100%110%
FlushAsync(...)0%220%
ReadAsync(...)100%110%
ReadAsync(...)100%110%
BeginRead(...)100%110%
EndRead(...)100%110%
WriteAsync(...)100%110%
WriteAsync(...)100%110%
BeginWrite(...)100%110%
EndWrite(...)100%110%
Read(...)100%110%
Write(...)100%110%
Seek(...)100%110%
SetLength(...)100%110%
.ctor(...)100%110%
WriteAsync(...)0%660%
ReadAsync(...)0%10100%
DisposeAsync()0%14140%
.ctor(...)100%110%
WriteAsync(...)0%660%
DisposeAsync()0%220%
.ctor(...)100%110%
ReadAsync(...)0%14140%
DisposeAsync()0%440%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Net.WebSockets/src/System/Net/WebSockets/WebSocketStream.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Diagnostics;
 5using System.Diagnostics.CodeAnalysis;
 6using System.IO;
 7using System.Runtime.ExceptionServices;
 8using System.Threading;
 9using System.Threading.Tasks;
 10
 11namespace System.Net.WebSockets
 12{
 13    /// <summary>Provides a <see cref="Stream"/> that delegates to a wrapped <see cref="WebSocket"/>.</summary>
 14    public class WebSocketStream : Stream
 15    {
 16        /// <summary>The default number of seconds before canceling CloseAsync operation issued during stream disposal.<
 17        private const int DefaultCloseTimeoutSeconds = 16;
 18
 19        /// <summary>Whether the stream has been disposed.</summary>
 20        private bool _disposed;
 21
 22        /// <summary>
 23        /// Initializes a new instance of the <see cref="WebSocketStream"/> class using a specified <see cref="WebSocket
 24        /// </summary>
 25        /// <param name="webSocket">The <see cref="WebSocket"/> wrapped by this instance.</param>
 026        private WebSocketStream(WebSocket webSocket) => WebSocket = webSocket;
 27
 28        /// <summary>Creates a <see cref="WebSocketStream"/> that delegates to a wrapped <see cref="WebSocket"/>.</summa
 29        /// <param name="webSocket">The wrapped <see cref="WebSocket"/>.</param>
 30        /// <param name="writeMessageType">The type of messages that should be written as part of <see cref="M:Stream.Wr
 31        /// <param name="ownsWebSocket">
 32        /// <see langword="true"/> if disposing the <see cref="Stream"/> should close the underlying <see cref="WebSocke
 33        /// </param>
 34        /// <returns>A new instance of <see cref="WebSocketStream"/> that forwards reads and writes on the <see cref="St
 35        public static WebSocketStream Create(WebSocket webSocket, WebSocketMessageType writeMessageType, bool ownsWebSoc
 036        {
 037            ArgumentNullException.ThrowIfNull(webSocket);
 038            ManagedWebSocket.ThrowIfInvalidMessageType(writeMessageType);
 39
 040            return new ReadWriteStream(
 041                webSocket,
 042                writeMessageType,
 043                closeTimeout: ownsWebSocket ? TimeSpan.FromSeconds(DefaultCloseTimeoutSeconds) : null);
 044        }
 45
 46        /// <summary>Creates a <see cref="WebSocketStream"/> that delegates to a wrapped <see cref="WebSocket"/>.</summa
 47        /// <param name="webSocket">The wrapped <see cref="WebSocket"/>.</param>
 48        /// <param name="writeMessageType">The type of messages that should be written as part of <see cref="M:Stream.Wr
 49        /// <param name="closeTimeout">The amount of time that disposing the <see cref="WebSocketStream"/> will wait for
 50        /// <returns>A new instance of <see cref="WebSocketStream"/> that forwards reads and writes on the <see cref="St
 51        public static WebSocketStream Create(WebSocket webSocket, WebSocketMessageType writeMessageType, TimeSpan closeT
 052        {
 053            ArgumentNullException.ThrowIfNull(webSocket);
 054            ManagedWebSocket.ThrowIfInvalidMessageType(writeMessageType);
 055            if (closeTimeout < TimeSpan.Zero && closeTimeout != Timeout.InfiniteTimeSpan)
 056            {
 057                throw new ArgumentOutOfRangeException(nameof(closeTimeout), SR.net_WebSockets_TimeoutOutOfRange);
 58            }
 59
 060            return new ReadWriteStream(webSocket, writeMessageType, closeTimeout);
 061        }
 62
 63        /// <summary>Creates a <see cref="WebSocketStream"/> that writes a single message to the underlying <see cref="W
 64        /// <param name="webSocket">The wrapped <see cref="WebSocket"/>.</param>
 65        /// <param name="writeMessageType">
 66        /// The type of messages that should be written as part of <see cref="M:Stream.WriteAsync"/> calls.
 67        /// Each write on the <see cref="Stream"/> results in writing a partial message to the underlying <see cref="Web
 68        /// When the <see cref="Stream"/> is disposed, it will write an empty message to the underlying <see cref="WebSo
 69        /// </param>
 70        /// <returns>A new instance of <see cref="WebSocketStream"/> that forwards writes on the <see cref="Stream"/> to
 71        public static WebSocketStream CreateWritableMessageStream(WebSocket webSocket, WebSocketMessageType writeMessage
 072        {
 073            ArgumentNullException.ThrowIfNull(webSocket);
 074            ManagedWebSocket.ThrowIfInvalidMessageType(writeMessageType);
 75
 076            return new WriteMessageStream(webSocket, writeMessageType);
 077        }
 78
 79        /// <summary>Creates a <see cref="WebSocketStream"/> that reads a single message from the underlying <see cref="
 80        /// <param name="webSocket">The wrapped <see cref="WebSocket"/>.</param>
 81        /// <returns>A new instance of <see cref="WebSocketStream"/> that forwards reads on the <see cref="Stream"/> to 
 82        /// <remarks>
 83        /// Reads on the <see cref="Stream"/> will read a single message from the underlying <see cref="WebSocket"/>. Th
 84        /// 0 bytes read once all data has been consumed from the next message received in the <see cref="WebSocket"/>.
 85        /// </remarks>
 86        public static WebSocketStream CreateReadableMessageStream(WebSocket webSocket)
 087        {
 088            ArgumentNullException.ThrowIfNull(webSocket);
 89
 090            return new ReadMessageStream(webSocket);
 091        }
 92
 93        /// <summary>Gets the underlying <see cref="WebSocket"/> wrapped by this <see cref="WebSocketStream"/>.</summary
 94        /// <remarks>The <see cref="WebSocket"/> used to construct this instance.</remarks>
 095        public WebSocket WebSocket { get; }
 96
 97        /// <inheritdoc />
 098        public override bool CanRead => !_disposed && WebSocket.State is WebSocketState.Open or WebSocketState.CloseSent
 99
 100        /// <inheritdoc />
 0101        public override bool CanWrite => !_disposed && WebSocket.State is WebSocketState.Open or WebSocketState.CloseRec
 102
 103        /// <inheritdoc />
 0104        public override bool CanSeek => false;
 105
 106        /// <inheritdoc />
 107        protected override void Dispose(bool disposing)
 0108        {
 0109            if (disposing)
 0110            {
 111                // There are no synchronous operations on WebSocket, so we're forced to do sync-over-async.
 0112                DisposeAsync().AsTask().GetAwaiter().GetResult();
 0113            }
 0114        }
 115
 116        /// <inheritdoc />
 0117        public override void Flush() { }
 118
 119        /// <inheritdoc />
 120        public override Task FlushAsync(CancellationToken cancellationToken) =>
 0121            cancellationToken.IsCancellationRequested ? Task.FromCanceled(cancellationToken) :
 0122            Task.CompletedTask;
 123
 124        /// <inheritdoc />
 125        public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0126        {
 0127            ValidateBufferArguments(buffer, offset, count);
 128
 0129            return ReadAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 0130        }
 131
 132        /// <inheritdoc />
 133        public override ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default) =>
 0134            ValueTask.FromException<int>(ExceptionDispatchInfo.SetCurrentStackTrace(new NotSupportedException()));
 135
 136        /// <inheritdoc />
 137        public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? st
 0138            TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count), callback, state);
 139
 140        /// <inheritdoc />
 141        public override int EndRead(IAsyncResult asyncResult) =>
 0142            TaskToAsyncResult.End<int>(asyncResult);
 143
 144        /// <inheritdoc />
 145        public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 0146        {
 0147            ValidateBufferArguments(buffer, offset, count);
 148
 0149            return WriteAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
 0150        }
 151
 152        /// <inheritdoc />
 153        public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = default)
 0154            ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(new NotSupportedException()));
 155
 156        /// <inheritdoc />
 157        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? s
 0158            TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count), callback, state);
 159
 160        /// <inheritdoc />
 161        public override void EndWrite(IAsyncResult asyncResult) =>
 0162            TaskToAsyncResult.End(asyncResult);
 163
 164        /// <inheritdoc />
 165        public override int Read(byte[] buffer, int offset, int count) =>
 0166            ReadAsync(buffer, offset, count, default).GetAwaiter().GetResult();
 167
 168        /// <inheritdoc />
 169        public override void Write(byte[] buffer, int offset, int count) =>
 0170            WriteAsync(buffer, offset, count, default).GetAwaiter().GetResult();
 171
 172        /// <inheritdoc />
 0173        public override long Length => throw new NotSupportedException();
 174
 175        /// <inheritdoc />
 176        public override long Position
 177        {
 0178            get => throw new NotSupportedException();
 0179            set => throw new NotSupportedException();
 180        }
 181
 182        /// <inheritdoc />
 0183        public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
 184
 185        /// <inheritdoc />
 0186        public override void SetLength(long value) => throw new NotSupportedException();
 187
 188        /// <summary>Provides stream that wraps a <see cref="WebSocket"/> and forwards reads/writes.</summary>
 0189        private sealed class ReadWriteStream(WebSocket webSocket, WebSocketMessageType writeMessageType, TimeSpan? close
 190        {
 0191            private readonly WebSocketMessageType _messageType = writeMessageType;
 0192            private readonly TimeSpan? _closeTimeout = closeTimeout;
 193
 194            /// <inheritdoc />
 195            public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = defa
 0196            {
 0197                if (_disposed)
 0198                {
 0199                    return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(new ObjectDisposedExceptio
 200                }
 201
 0202                if (!CanWrite)
 0203                {
 0204                    return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(new NotSupportedException(
 205                }
 206
 0207                if (cancellationToken.IsCancellationRequested)
 0208                {
 0209                    return ValueTask.FromCanceled(cancellationToken);
 210                }
 211
 0212                return WebSocket.SendAsync(buffer, _messageType, endOfMessage: true, cancellationToken);
 0213            }
 214
 215            /// <inheritdoc />
 216            public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = de
 0217            {
 0218                ObjectDisposedException.ThrowIf(_disposed, this);
 219
 0220                if (!CanRead)
 0221                {
 0222                    throw new NotSupportedException(SR.NotReadableStream);
 223                }
 224
 0225                cancellationToken.ThrowIfCancellationRequested();
 226
 0227                while (WebSocket.State < WebSocketState.CloseReceived)
 0228                {
 0229                    ValueWebSocketReceiveResult result = await WebSocket.ReceiveAsync(buffer, cancellationToken).Configu
 0230                    if (result.MessageType is WebSocketMessageType.Close)
 0231                    {
 0232                        break;
 233                    }
 234
 0235                    if (result.Count > 0 || buffer.IsEmpty)
 0236                    {
 0237                        return result.Count;
 238                    }
 0239                }
 240
 0241                return 0;
 0242            }
 243
 244            /// <inheritdoc />
 245            public override async ValueTask DisposeAsync()
 0246            {
 0247                if (!_disposed)
 0248                {
 0249                    _disposed = true;
 250
 0251                    if (_closeTimeout is { } timeout)
 0252                    {
 0253                        if (WebSocket.State is < WebSocketState.Closed)
 0254                        {
 0255                            CancellationTokenSource? cts = null;
 256                            CancellationToken ct;
 257
 0258                            if (timeout == default)
 0259                            {
 0260                                ct = new CancellationToken(canceled: true);
 0261                            }
 0262                            else if (timeout == Timeout.InfiniteTimeSpan)
 0263                            {
 0264                                ct = CancellationToken.None;
 0265                            }
 266                            else
 0267                            {
 0268                                cts = new CancellationTokenSource(timeout);
 0269                                ct = cts.Token;
 0270                            }
 271
 272                            try
 0273                            {
 0274                                await WebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, null, ct).ConfigureAwait(
 0275                            }
 276                            finally
 0277                            {
 0278                                cts?.Dispose();
 0279                            }
 0280                        }
 281
 0282                        WebSocket.Dispose();
 0283                    }
 0284                }
 0285            }
 286        }
 287
 288        /// <summary>Provides a stream that wraps a <see cref="WebSocket"/> and writes a single message.</summary>
 0289        private sealed class WriteMessageStream(WebSocket webSocket, WebSocketMessageType writeMessageType) : WebSocketS
 290        {
 0291            private readonly WebSocketMessageType _messageType = writeMessageType;
 292
 293            /// <inheritdoc />
 0294            public override bool CanRead => false;
 295
 296            /// <inheritdoc />
 297            public override ValueTask WriteAsync(ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken = defa
 0298            {
 0299                if (_disposed)
 0300                {
 0301                    return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(new ObjectDisposedExceptio
 302                }
 303
 0304                if (!CanWrite)
 0305                {
 0306                    return ValueTask.FromException(ExceptionDispatchInfo.SetCurrentStackTrace(new NotSupportedException(
 307                }
 308
 0309                if (cancellationToken.IsCancellationRequested)
 0310                {
 0311                    return ValueTask.FromCanceled(cancellationToken);
 312                }
 313
 0314                return WebSocket.SendAsync(buffer, _messageType, endOfMessage: false, cancellationToken);
 0315            }
 316
 317            public override ValueTask DisposeAsync()
 0318            {
 0319                if (!_disposed)
 0320                {
 0321                    _disposed = true;
 0322                    return WebSocket.SendAsync(ReadOnlyMemory<byte>.Empty, _messageType, endOfMessage: true, Cancellatio
 323                }
 324
 0325                return default;
 0326            }
 327        }
 328
 329        /// <summary>Provides a stream that wraps a <see cref="WebSocket"/> and reads a single message.</summary>
 0330        private sealed class ReadMessageStream(WebSocket webSocket) : WebSocketStream(webSocket)
 331        {
 332            /// <summary>Whether we've seen and end-of-message marker.</summary>
 333            private bool _eof;
 334
 335            /// <inheritdoc />
 0336            public override bool CanWrite => false;
 337
 338            /// <inheritdoc />
 339            public override async ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = de
 0340            {
 0341                ObjectDisposedException.ThrowIf(_disposed, this);
 342
 0343                if (!CanRead)
 0344                {
 0345                    throw new NotSupportedException(SR.NotReadableStream);
 346                }
 347
 0348                cancellationToken.ThrowIfCancellationRequested();
 349
 0350                while (!_eof && WebSocket.State < WebSocketState.CloseReceived)
 0351                {
 0352                    ValueWebSocketReceiveResult result = await WebSocket.ReceiveAsync(buffer, cancellationToken).Configu
 0353                    if (result.MessageType is WebSocketMessageType.Close)
 0354                    {
 0355                        break;
 356                    }
 357
 0358                    if (result.EndOfMessage)
 0359                    {
 0360                        _eof = true;
 0361                    }
 362
 0363                    if (result.Count > 0 || buffer.IsEmpty)
 0364                    {
 0365                        return result.Count;
 366                    }
 0367                }
 368
 0369                return 0;
 0370            }
 371
 372            /// <inheritdoc />
 373            public override ValueTask DisposeAsync()
 0374            {
 0375                _disposed = true;
 0376                if (!_eof && WebSocket.State < WebSocketState.CloseReceived)
 0377                {
 0378                    WebSocket.Abort();
 0379                }
 0380                return default;
 0381            }
 382        }
 383    }
 384}
 385

Methods/Properties

.ctor(System.Net.WebSockets.WebSocket)
Create(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketMessageType,System.Boolean)
Create(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketMessageType,System.TimeSpan)
CreateWritableMessageStream(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketMessageType)
CreateReadableMessageStream(System.Net.WebSockets.WebSocket)
WebSocket()
CanRead()
CanWrite()
CanSeek()
Dispose(System.Boolean)
Flush()
FlushAsync(System.Threading.CancellationToken)
ReadAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
ReadAsync(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
BeginRead(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
EndRead(System.IAsyncResult)
WriteAsync(System.Byte[],System.Int32,System.Int32,System.Threading.CancellationToken)
WriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
BeginWrite(System.Byte[],System.Int32,System.Int32,System.AsyncCallback,System.Object)
EndWrite(System.IAsyncResult)
Read(System.Byte[],System.Int32,System.Int32)
Write(System.Byte[],System.Int32,System.Int32)
Length()
Position()
Position(System.Int64)
Seek(System.Int64,System.IO.SeekOrigin)
SetLength(System.Int64)
.ctor(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketMessageType,System.Nullable`1<System.TimeSpan>)
WriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
ReadAsync(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
DisposeAsync()
.ctor(System.Net.WebSockets.WebSocket,System.Net.WebSockets.WebSocketMessageType)
CanRead()
WriteAsync(System.ReadOnlyMemory`1<System.Byte>,System.Threading.CancellationToken)
DisposeAsync()
.ctor(System.Net.WebSockets.WebSocket)
CanWrite()
ReadAsync(System.Memory`1<System.Byte>,System.Threading.CancellationToken)
DisposeAsync()