-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Description
Background and motivation
It's not possible for an app hosting multiple named pipe connections to verify that no one else is using the pipe name when it starts.
For example, Kestrel is looking at adding a named pipe transport, and when we start Kestrel we want to verify that no one else is using the configured pipe name:
- We don't want to share the pipe between Kestrel instances. It's common for devs to accidentally launch Kestrel when it's already running. A socket errors if you bind to it when it's already open. We want the same protection with a named pipe.
- A pipe gets many of its settings when it is first opened. Want to verify that Kestrel's settings are applied to the pipe.
Named pipes have a built-in option for checking this - FILE_FLAG_FIRST_PIPE_INSTANCE. See https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
.NET doesn't expose this option. While it is automatically set if the max number of pipe instances is 1, there is no way to explicitly set this option.
runtime/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeServerStream.Windows.cs
Lines 121 to 124 in c59b517
int openMode = ((int)direction) | | |
(maxNumberOfServerInstances == 1 ? Interop.Kernel32.FileOperations.FILE_FLAG_FIRST_PIPE_INSTANCE : 0) | | |
(int)options | | |
(int)additionalAccessRights; |
If the PipeOptions.FirstPipeInstance
options was available, then Kestrel would create the first NamedPipeServerStream
with it the option to verify that there is no existing pipe with that name and create subsequent NamedPipeServerStream
instances without the option.
Note that validation on the NamedPipeServerStream
ctor currently prevents the enum value having FILE_FLAG_FIRST_PIPE_INSTANCE value added to it. e.g.
// NamedPipeServerStream errors when passed this pipe option
pipeOptions |= (PipeOptions)0x00080000;
API Proposal
namespace System.IO.Pipes
{
[Flags]
public enum PipeOptions
{
FirstPipeInstance = unchecked((int)0x00080000)
}
}
API Usage
private NamedPipeServerStream CreateServerStream(bool firstStream)
{
var pipeOptions = PipeOptions.Asynchronous | PipeOptions.WriteThrough;
if (_options.CurrentUserOnly)
{
pipeOptions |= PipeOptions.CurrentUserOnly;
}
if (firstStream)
{
pipeOptions |= PipeOptions.FirstPipeInstance;
}
return new NamedPipeServerStream(
_endpoint.PipeName,
PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Byte,
pipeOptions,
inBufferSize: 0,
outBufferSize: 0);
}
Alternative Designs
[Flags]
public enum PipeOptions
{
EnsureFirstPipeInstance = unchecked((int)0x00080000)
}
It still alludes at the name of the Windows API flag and conveys "throw if already exists".
Risks
No response