< Summary - CoreWCF Coverage — PR #1766

Information
Class: CoreWCF.Channels.PipeStreamHelper
Assembly: CoreWCF.NetNamedPipe
File(s): /home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetNamedPipe/src/CoreWCF/Channels/PipeStreamHelper.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 78
Coverable lines: 78
Total lines: 197
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 16
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%110%
CreatePipeStream(...)100%110%
WriteZeroAsync(...)0%440%
IOCallback(...)0%220%
CancellationCallback(...)100%110%
CreateWriteException(...)100%110%
CreateException(...)100%110%
.ctor(...)100%110%
CreatePipe(...)0%10100%

File(s)

/home/runner/work/CoreWCF/CoreWCF/src/CoreWCF.NetNamedPipe/src/CoreWCF/Channels/PipeStreamHelper.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;
 5using System.ComponentModel;
 6using System.IO.Pipes;
 7using System.Runtime.InteropServices;
 8using System.Runtime.Versioning;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using CoreWCF.IO;
 12using CoreWCF.Runtime;
 13using CoreWCF.Security;
 14using Microsoft.Extensions.DependencyInjection;
 15using Microsoft.Extensions.Logging;
 16using Microsoft.Win32.SafeHandles;
 17
 18namespace CoreWCF.Channels
 19{
 20    [SupportedOSPlatform("windows")]
 21    internal static class PipeStreamHelper
 22    {
 023        internal static readonly Action<object> s_cancellationCallback = CancellationCallback;
 24
 25        public static NamedPipeServerStream CreatePipeStream(NamedPipeListenOptions options, string pipeName, ref bool f
 26        {
 027            var handle = CreatePipe(options, pipeName, ref firstConnection);
 028            return new NamedPipeServerStream(PipeDirection.InOut, isAsync: true, isConnected: false, handle);
 29        }
 30
 31        public static unsafe Task WriteZeroAsync(this NamedPipeServerStream pipeStream, CancellationToken cancellationTo
 32        {
 033            byte[] zeroByteBuffer = new byte[0];
 034            Overlapped overlapped = new Overlapped();
 035            var zeroByteGCHandle = GCHandle.Alloc(zeroByteBuffer, GCHandleType.Pinned);
 036            var stateHolder = new StateHolder(zeroByteGCHandle);
 037            CancellationTokenRegistration cancellationRegistration = default;
 038            var nativeOverlapped = overlapped.Pack(IOCallback, stateHolder);
 39            // Queue an async WriteFile operation.
 040            if (UnsafeNativeMethods.WriteFile(pipeStream.SafePipeHandle, ref zeroByteBuffer, 0, IntPtr.Zero, nativeOverl
 41            {
 42                // The operation failed, or it's pending.
 043                int error = Marshal.GetLastWin32Error();
 44                switch (error)
 45                {
 46                    case UnsafeNativeMethods.ERROR_IO_PENDING:
 47                        // Common case: IO was initiated, completion will be handled by callback.
 48                        // Register for cancellation now that the operation has been initiated.
 049                        cancellationRegistration = cancellationToken.Register(s_cancellationCallback, (stateHolder.TaskC
 50                        // Need to cleanup cancellation registration after the Task completes.
 051                        return stateHolder.TaskCompletionSource.Task.ContinueWith((task, state) => { ((CancellationToken
 52                    default:
 53                        // Error. Callback will not be invoked.
 054                        Overlapped.Unpack(nativeOverlapped);
 055                        zeroByteGCHandle.Free();
 056                        throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateWriteException(error));
 57                }
 58            }
 59            else
 60            {
 61                // WriteFile returned a non-zero result which means it completed execution synchronously.
 62                // Need to cleanup the zero byte memory handle, but not the cancellation registration
 63                // as that only gets registered when we go async.
 064                stateHolder.TaskCompletionSource.TrySetResult(null);
 065                zeroByteGCHandle.Free();
 066                Overlapped.Unpack(nativeOverlapped);
 067                return stateHolder.TaskCompletionSource.Task;
 68            }
 69        }
 70
 71        private static unsafe void IOCallback(uint errorCode, uint numBytes, NativeOverlapped* pOverlapped)
 72        {
 073            Overlapped overlapped = Overlapped.Unpack(pOverlapped);
 074            var stateHolder = (StateHolder)overlapped.AsyncResult;
 075            if (errorCode != 0)
 76            {
 077                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreateWriteException((int)errorCode));
 78            }
 079            stateHolder.GCHandle.Free();
 080            stateHolder.TaskCompletionSource.TrySetResult(null);
 081        }
 82
 83        private static void CancellationCallback(object obj)
 84        {
 085            var state = ((TaskCompletionSource<object> tcs, CancellationToken cancellationToken))obj;
 086            state.tcs.TrySetCanceled(state.cancellationToken);
 087        }
 88
 89        private static PipeException CreateWriteException(int error)
 90        {
 091            return CreateException(SR.PipeWriteError, error);
 92        }
 93
 94        private static PipeException CreateException(string resourceString, int error)
 95        {
 096            return new PipeException(SR.Format(resourceString, PipeError.GetErrorString(error)), error);
 97        }
 98
 99        private class StateHolder : IAsyncResult
 100        {
 0101            public StateHolder(GCHandle gcHandle)
 102            {
 0103                GCHandle = gcHandle;
 0104            }
 105
 0106            public TaskCompletionSource<object> TaskCompletionSource { get; } = new TaskCompletionSource<object>();
 0107            public GCHandle GCHandle { get; }
 0108            public object AsyncState => throw Fx.AssertAndThrow("StateHolder.AsyncState called.");
 0109            public WaitHandle AsyncWaitHandle => throw Fx.AssertAndThrow("StateHolder.AsyncWaitHandle called.");
 0110            public bool CompletedSynchronously => throw Fx.AssertAndThrow("StateHolder.CompletedSynchronously called.");
 0111            public bool IsCompleted => throw Fx.AssertAndThrow("StateHolder.IsCompleted called.");
 112        }
 113
 114        private static SafePipeHandle CreatePipe(NamedPipeListenOptions options, string pipeName, ref bool firstConnecti
 115        {
 0116            var loggerFactory = options.ApplicationServices.GetRequiredService<ILoggerFactory>();
 0117            var logger = loggerFactory.CreateLogger(typeof(PipeStreamHelper).FullName);
 0118            int openMode = UnsafeNativeMethods.PIPE_ACCESS_DUPLEX | UnsafeNativeMethods.FILE_FLAG_OVERLAPPED;
 0119            if (firstConnection)
 120            {
 0121                openMode |= UnsafeNativeMethods.FILE_FLAG_FIRST_PIPE_INSTANCE;
 122            }
 123
 124            byte[] binarySecurityDescriptor;
 125
 126            try
 127            {
 0128                binarySecurityDescriptor = SecurityDescriptorHelper.FromSecurityIdentifiers(options.InternalAllowedUsers
 0129            }
 0130            catch (Win32Exception e)
 131            {
 132                // While Win32exceptions are not expected, if they do occur we need to obey the pipe/communication excep
 0133                Exception innerException = new PipeException(e.Message, e);
 0134                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(innerException.Mess
 135            }
 136
 137            SafePipeHandle pipeHandle;
 0138            GCHandle binarySecurityDescriptorHandle = default;
 139            int error;
 140            try
 141            {
 0142                binarySecurityDescriptorHandle = GCHandle.Alloc(binarySecurityDescriptor, GCHandleType.Pinned);
 0143                UnsafeNativeMethods.SECURITY_ATTRIBUTES securityAttributes = new UnsafeNativeMethods.SECURITY_ATTRIBUTES
 144                // TODO: Try replacing lpSecurityDescriptor with byte[]. I think we can avoid pinning.
 0145                securityAttributes.lpSecurityDescriptor = binarySecurityDescriptorHandle.AddrOfPinnedObject();
 146
 0147                pipeHandle = UnsafeNativeMethods.CreateNamedPipe(
 0148                                                    pipeName,
 0149                                                    openMode,
 0150                                                    UnsafeNativeMethods.PIPE_TYPE_MESSAGE | UnsafeNativeMethods.PIPE_REA
 0151                                                    UnsafeNativeMethods.PIPE_UNLIMITED_INSTANCES,
 0152                                                    options.ConnectionBufferSize,
 0153                                                    options.ConnectionBufferSize, 0, securityAttributes);
 0154                error = Marshal.GetLastWin32Error();
 0155            }
 156            finally
 157            {
 0158                if (binarySecurityDescriptorHandle.IsAllocated)
 159                {
 0160                    binarySecurityDescriptorHandle.Free();
 161                }
 0162            }
 163
 0164            if (pipeHandle.IsInvalid)
 165            {
 0166                pipeHandle.SetHandleAsInvalid();
 167
 0168                Exception innerException = new PipeException(SR.Format(SR.PipeListenFailed,
 0169                    options.BaseAddress.AbsoluteUri, PipeError.GetErrorString(error)), error);
 170
 0171                if (error == UnsafeNativeMethods.ERROR_ACCESS_DENIED)
 172                {
 0173                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AddressAccessDeniedException(innerExce
 174                }
 0175                else if (error == UnsafeNativeMethods.ERROR_ALREADY_EXISTS)
 176                {
 0177                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AddressAlreadyInUseException(innerExce
 178                }
 179                else
 180                {
 0181                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(innerException.
 182                }
 183            }
 184            //else
 185            //{
 186            //    if (TD.NamedPipeCreatedIsEnabled())
 187            //    {
 188            //        TD.NamedPipeCreated(pipeName);
 189            //    }
 190            //}
 191
 0192            firstConnection = false;
 0193            return pipeHandle;
 194        }
 195
 196    }
 197}