File: System\Net\Quic\QuicStream.Stream.cs
Web Access
Project: src\src\libraries\System.Net.Quic\src\System.Net.Quic.csproj (System.Net.Quic)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Buffers;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
 
namespace System.Net.Quic;
 
// Boilerplate implementation of Stream methods.
public partial class QuicStream : Stream
{
    // Seek and length.
    /// <inheritdoc />
    /// <summary>Gets a value indicating whether the <see cref="QuicStream" /> supports seeking.</summary>
    public override bool CanSeek => false;
 
    /// <inheritdoc />
    /// <summary>Gets the length of the data available on the stream. This property is not currently supported and always throws a <see cref="NotSupportedException" />.</summary>
    /// <exception cref="NotSupportedException">In all cases.</exception>
    public override long Length => throw new NotSupportedException();
 
    /// <inheritdoc />
    /// <summary>Gets or sets the position within the current stream. This property is not currently supported and always throws a <see cref="NotSupportedException" />.</summary>
    /// <exception cref="NotSupportedException">In all cases.</exception>
    public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); }
 
    /// <inheritdoc />
    /// <summary>Sets the current position of the stream to the given value. This method is not currently supported and always throws a <see cref="NotSupportedException" />.</summary>
    /// <exception cref="NotSupportedException">In all cases.</exception>
    public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException();
 
    /// <inheritdoc />
    /// <summary>Sets the length of the stream. This method is not currently supported and always throws a <see cref="NotSupportedException" />.</summary>
    /// <exception cref="NotSupportedException">In all cases.</exception>
    public override void SetLength(long value) => throw new NotSupportedException();
 
    // Read and Write timeouts.
    /// <inheritdoc />
    /// <summary>Gets a value that indicates whether the <see cref="QuicStream" /> can timeout.</summary>
    public override bool CanTimeout => true;
 
    private TimeSpan _readTimeout = Timeout.InfiniteTimeSpan;
    private TimeSpan _writeTimeout = Timeout.InfiniteTimeSpan;
 
    /// <inheritdoc />
    public override int ReadTimeout
    {
        get
        {
            ObjectDisposedException.ThrowIf(_disposed == 1, this);
            return (int)_readTimeout.TotalMilliseconds;
        }
        set
        {
            ObjectDisposedException.ThrowIf(_disposed == 1, this);
            if (value <= 0 && value != Timeout.Infinite)
            {
                throw new ArgumentOutOfRangeException(nameof(value), SR.net_quic_timeout_use_gt_zero);
            }
            _readTimeout = TimeSpan.FromMilliseconds(value);
        }
    }
 
    /// <inheritdoc />
    public override int WriteTimeout
    {
        get
        {
            ObjectDisposedException.ThrowIf(_disposed == 1, this);
            return (int)_writeTimeout.TotalMilliseconds;
        }
        set
        {
            ObjectDisposedException.ThrowIf(_disposed == 1, this);
            if (value <= 0 && value != Timeout.Infinite)
            {
                throw new ArgumentOutOfRangeException(nameof(value), SR.net_quic_timeout_use_gt_zero);
            }
            _writeTimeout = TimeSpan.FromMilliseconds(value);
        }
    }
 
    // Read boilerplate.
    /// <inheritdoc />
    /// <summary>Gets a value indicating whether the <see cref="QuicStream" /> supports reading.</summary>
    public override bool CanRead => Volatile.Read(ref _disposed) == 0 && _canRead;
 
    /// <inheritdoc />
    public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
        => TaskToAsyncResult.Begin(ReadAsync(buffer, offset, count, default), callback, state);
 
    /// <inheritdoc />
    public override int EndRead(IAsyncResult asyncResult)
        => TaskToAsyncResult.End<int>(asyncResult);
 
    /// <inheritdoc />
    public override int Read(byte[] buffer, int offset, int count)
    {
        ValidateBufferArguments(buffer, offset, count);
        return Read(buffer.AsSpan(offset, count));
    }
 
    /// <inheritdoc />
    public override int ReadByte()
    {
        byte b = 0;
        return Read(new Span<byte>(ref b)) != 0 ? b : -1;
    }
 
    /// <inheritdoc />
    public override int Read(Span<byte> buffer)
    {
        ObjectDisposedException.ThrowIf(_disposed == 1, this);
 
        byte[] rentedBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length);
        CancellationTokenSource? cts = null;
        try
        {
            if (_readTimeout > TimeSpan.Zero)
            {
                cts = new CancellationTokenSource(_readTimeout);
            }
            int readLength = ReadAsync(new Memory<byte>(rentedBuffer, 0, buffer.Length), cts?.Token ?? default).AsTask().GetAwaiter().GetResult();
            rentedBuffer.AsSpan(0, readLength).CopyTo(buffer);
            return readLength;
        }
        catch (OperationCanceledException) when (cts?.IsCancellationRequested == true)
        {
            // sync operations do not have Cancellation
            throw new IOException(SR.net_quic_timeout);
        }
        finally
        {
            ArrayPool<byte>.Shared.Return(rentedBuffer);
            cts?.Dispose();
        }
    }
 
    /// <inheritdoc />
    public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
    {
        ValidateBufferArguments(buffer, offset, count);
        return ReadAsync(new Memory<byte>(buffer, offset, count), cancellationToken).AsTask();
    }
 
    // Write boilerplate.
    /// <inheritdoc />
    /// <summary>Gets a value indicating whether the <see cref="QuicStream" /> supports writing.</summary>
    public override bool CanWrite => Volatile.Read(ref _disposed) == 0 && _canWrite;
 
    /// <inheritdoc />
    public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
        => TaskToAsyncResult.Begin(WriteAsync(buffer, offset, count, default), callback, state);
 
    /// <inheritdoc />
    public override void EndWrite(IAsyncResult asyncResult)
        => TaskToAsyncResult.End(asyncResult);
 
    /// <inheritdoc />
    public override void Write(byte[] buffer, int offset, int count)
    {
        ValidateBufferArguments(buffer, offset, count);
        Write(buffer.AsSpan(offset, count));
    }
 
    /// <inheritdoc />
    public override void WriteByte(byte value)
    {
        Write(new ReadOnlySpan<byte>(in value));
    }
 
    /// <inheritdoc />
    public override void Write(ReadOnlySpan<byte> buffer)
    {
        ObjectDisposedException.ThrowIf(_disposed == 1, this);
 
        CancellationTokenSource? cts = null;
        if (_writeTimeout > TimeSpan.Zero)
        {
            cts = new CancellationTokenSource(_writeTimeout);
        }
        try
        {
            WriteAsync(buffer.ToArray(), cts?.Token ?? default).AsTask().GetAwaiter().GetResult();
        }
        catch (OperationCanceledException) when (cts?.IsCancellationRequested == true)
        {
            // sync operations do not have Cancellation
            throw new IOException(SR.net_quic_timeout);
        }
        finally
        {
            cts?.Dispose();
        }
    }
 
    /// <inheritdoc />
    public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
    {
        ValidateBufferArguments(buffer, offset, count);
        return WriteAsync(new ReadOnlyMemory<byte>(buffer, offset, count), cancellationToken).AsTask();
    }
 
    // Flush.
 
    /// <inheritdoc />
    public override void Flush()
        => FlushAsync().GetAwaiter().GetResult();
 
    /// <inheritdoc />
    public override Task FlushAsync(CancellationToken cancellationToken = default)
        // NOP for now
        => Task.CompletedTask;
 
    // Dispose.
    /// <inheritdoc />
    protected override void Dispose(bool disposing)
    {
        DisposeAsync().AsTask().GetAwaiter().GetResult();
        base.Dispose(disposing);
    }
}