|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Win32.SafeHandles;
namespace System.IO.Pipes
{
public abstract partial class PipeStream : Stream
{
internal const string AnonymousPipeName = "anonymous";
private SafePipeHandle? _handle;
private bool _canRead;
private bool _canWrite;
private bool _isAsync;
private bool _isCurrentUserOnly;
private bool _isMessageComplete;
private bool _isFromExistingHandle;
private bool _isHandleExposed;
private PipeTransmissionMode _readMode;
private PipeTransmissionMode _transmissionMode;
private PipeDirection _pipeDirection;
private uint _outBufferSize;
private PipeState _state;
protected PipeStream(PipeDirection direction, int bufferSize)
{
if (direction < PipeDirection.In || direction > PipeDirection.InOut)
{
throw new ArgumentOutOfRangeException(nameof(direction), SR.ArgumentOutOfRange_DirectionModeInOutOrInOut);
}
ArgumentOutOfRangeException.ThrowIfNegative(bufferSize);
Init(direction, PipeTransmissionMode.Byte, (uint)bufferSize);
}
protected PipeStream(PipeDirection direction, PipeTransmissionMode transmissionMode, int outBufferSize)
{
if (direction < PipeDirection.In || direction > PipeDirection.InOut)
{
throw new ArgumentOutOfRangeException(nameof(direction), SR.ArgumentOutOfRange_DirectionModeInOutOrInOut);
}
if (transmissionMode < PipeTransmissionMode.Byte || transmissionMode > PipeTransmissionMode.Message)
{
throw new ArgumentOutOfRangeException(nameof(transmissionMode), SR.ArgumentOutOfRange_TransmissionModeByteOrMsg);
}
ArgumentOutOfRangeException.ThrowIfNegative(outBufferSize);
Init(direction, transmissionMode, (uint)outBufferSize);
}
private void Init(PipeDirection direction, PipeTransmissionMode transmissionMode, uint outBufferSize)
{
Debug.Assert(direction >= PipeDirection.In && direction <= PipeDirection.InOut, "invalid pipe direction");
Debug.Assert(transmissionMode >= PipeTransmissionMode.Byte && transmissionMode <= PipeTransmissionMode.Message, "transmissionMode is out of range");
Debug.Assert(outBufferSize >= 0, "outBufferSize is negative");
// always defaults to this until overridden
_readMode = transmissionMode;
_transmissionMode = transmissionMode;
_pipeDirection = direction;
if ((_pipeDirection & PipeDirection.In) != 0)
{
_canRead = true;
}
if ((_pipeDirection & PipeDirection.Out) != 0)
{
_canWrite = true;
}
_outBufferSize = outBufferSize;
// This should always default to true
_isMessageComplete = true;
_state = PipeState.WaitingToConnect;
}
// Once a PipeStream has a handle ready, it should call this method to set up the PipeStream. If
// the pipe is in a connected state already, it should also set the IsConnected (protected) property.
// This method may also be called to uninitialize a handle, setting it to null.
protected void InitializeHandle(SafePipeHandle? handle, bool isExposed, bool isAsync)
{
if (isAsync && handle != null)
{
InitializeAsyncHandle(handle);
}
_handle = handle;
_isAsync = isAsync;
// track these separately; _isHandleExposed will get updated if accessed though the property
_isHandleExposed = isExposed;
_isFromExistingHandle = isExposed;
}
[Conditional("DEBUG")]
private static void DebugAssertHandleValid(SafePipeHandle handle)
{
Debug.Assert(handle != null, "handle is null");
Debug.Assert(!handle.IsClosed, "handle is closed");
}
// Reads a byte from the pipe stream. Returns the byte cast to an int
// or -1 if the connection has been broken.
public override unsafe int ReadByte()
{
byte b;
return Read(new Span<byte>(&b, 1)) > 0 ? b : -1;
}
public override unsafe void WriteByte(byte value)
{
Write(new ReadOnlySpan<byte>(&value, 1));
}
public override void Flush()
{
CheckWriteOperations();
// Does nothing on PipeStreams. We cannot call Interop.FlushFileBuffers here because we can deadlock
// if the other end of the pipe is no longer interested in reading from the pipe.
}
public override Task FlushAsync(CancellationToken cancellationToken)
{
try
{
Flush();
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.FromException(ex);
}
}
protected override void Dispose(bool disposing)
{
try
{
// Nothing will be done differently based on whether we are
// disposing vs. finalizing.
if (_handle != null && !_handle.IsClosed)
{
_handle.Dispose();
}
DisposeCore(disposing);
}
finally
{
base.Dispose(disposing);
}
_state = PipeState.Closed;
}
// ********************** Public Properties *********************** //
// APIs use coarser definition of connected, but these map to internal
// Connected/Disconnected states. Note that setter is protected; only
// intended to be called by custom PipeStream concrete children
public bool IsConnected
{
get
{
return State == PipeState.Connected;
}
protected set
{
_state = (value) ? PipeState.Connected : PipeState.Disconnected;
}
}
public bool IsAsync
{
get { return _isAsync; }
}
// Set by the most recent call to Read or EndRead. Will be false if there are more buffer in the
// message, otherwise it is set to true.
public bool IsMessageComplete
{
get
{
// omitting pipe broken exception to allow reader to finish getting message
if (_state == PipeState.WaitingToConnect)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeNotYetConnected);
}
if (_state == PipeState.Disconnected)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeDisconnected);
}
if (CheckOperationsRequiresSetHandle && _handle == null)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeHandleNotSet);
}
if ((_state == PipeState.Closed) || (_handle != null && _handle.IsClosed))
{
throw Error.GetPipeNotOpen();
}
// don't need to check transmission mode; just care about read mode. Always use
// cached mode; otherwise could throw for valid message when other side is shutting down
if (_readMode != PipeTransmissionMode.Message)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeReadModeNotMessage);
}
return _isMessageComplete;
}
}
internal void UpdateMessageCompletion(bool completion)
{
// Set message complete to true because the pipe is broken as well.
// Need this to signal to readers to stop reading.
_isMessageComplete = (completion || _state == PipeState.Broken);
}
public SafePipeHandle SafePipeHandle
{
get
{
if (_handle == null)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeHandleNotSet);
}
if (_handle.IsClosed)
{
throw Error.GetPipeNotOpen();
}
_isHandleExposed = true;
return _handle;
}
}
internal SafePipeHandle? InternalHandle
{
get
{
return _handle;
}
}
protected bool IsHandleExposed
{
get
{
return _isHandleExposed;
}
}
public override bool CanRead
{
get
{
return _canRead;
}
}
public override bool CanWrite
{
get
{
return _canWrite;
}
}
public override bool CanSeek
{
get
{
return false;
}
}
public override long Length
{
get
{
throw Error.GetSeekNotSupported();
}
}
public override long Position
{
get
{
throw Error.GetSeekNotSupported();
}
set
{
throw Error.GetSeekNotSupported();
}
}
public override void SetLength(long value)
{
throw Error.GetSeekNotSupported();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw Error.GetSeekNotSupported();
}
// anonymous pipe ends and named pipe server can get/set properties when broken
// or connected. Named client overrides
protected internal virtual void CheckPipePropertyOperations()
{
if (CheckOperationsRequiresSetHandle && _handle == null)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeHandleNotSet);
}
// these throw object disposed
if ((_state == PipeState.Closed) || (_handle != null && _handle.IsClosed))
{
throw Error.GetPipeNotOpen();
}
}
// Reads can be done in Connected and Broken. In the latter,
// read returns 0 bytes
protected internal void CheckReadOperations()
{
// Invalid operation
if (_state == PipeState.WaitingToConnect)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeNotYetConnected);
}
if (_state == PipeState.Disconnected)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeDisconnected);
}
if (CheckOperationsRequiresSetHandle && _handle == null)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeHandleNotSet);
}
// these throw object disposed
if ((_state == PipeState.Closed) || (_handle != null && _handle.IsClosed))
{
throw Error.GetPipeNotOpen();
}
}
// Writes can only be done in connected state
protected internal void CheckWriteOperations()
{
// Invalid operation
if (_state == PipeState.WaitingToConnect)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeNotYetConnected);
}
if (_state == PipeState.Disconnected)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeDisconnected);
}
if (CheckOperationsRequiresSetHandle && _handle == null)
{
throw new InvalidOperationException(SR.InvalidOperation_PipeHandleNotSet);
}
// IOException
if (_state == PipeState.Broken)
{
throw new IOException(SR.IO_PipeBroken);
}
// these throw object disposed
if ((_state == PipeState.Closed) || (_handle != null && _handle.IsClosed))
{
throw Error.GetPipeNotOpen();
}
}
internal PipeState State
{
get
{
return _state;
}
set
{
_state = value;
}
}
internal bool IsCurrentUserOnly
{
get
{
return _isCurrentUserOnly;
}
set
{
_isCurrentUserOnly = value;
}
}
}
}
|