Class PipeConnection
- Namespace
- WebDriverBiDi.Protocol
- Assembly
- WebDriverBiDi.dll
Represents a connection to a WebDriver BiDi remote end over anonymous pipes. This is used with Chromium's --remote-debugging-pipe flag, which on non-Windows systems communicates via file descriptors 3 (browser reads) and 4 (browser writes).
public class PipeConnection : Connection, IAsyncDisposable
- Inheritance
-
PipeConnection
- Implements
- Inherited Members
Remarks
PipeConnection provides a specialized transport mechanism for browser communication using anonymous pipes instead of WebSockets. This offers slightly lower latency but requires the browser and application to be on the same machine.
When to consider pipe connections:
- High-performance local test suites where latency is critical
- Browser implementation supports --remote-debugging-pipe (currently only Chromium-based browsers)
- Browser and tests run on the same machine
Protocol details:
- Messages are null-terminated JSON strings (each message ends with \0)
- Two anonymous pipes are created on every platform and their handles are inherited by the browser process
- On Unix systems the browser reads from file descriptor 3 and writes to file descriptor 4; on Windows it receives the inherited handles
- Requires IPipeServerProcessProvider for process lifecycle management
Limitations:
- Only supported by Chromium-based browsers (Chrome, Edge)
- Cannot connect to remote browsers
- More complex setup than WebSocket connections
Recommendation: Most users should use WebSocketConnection instead. Pipe connections are only beneficial for specialized high-performance scenarios with Chromium browsers.
Constructors
PipeConnection(IPipeServerProcessProvider)
Initializes a new instance of the PipeConnection class.
public PipeConnection(IPipeServerProcessProvider processProvider)
Parameters
processProviderIPipeServerProcessProviderAn implementation of IPipeServerProcessProvider that provides a Process that is able to send and receive messages over pipe connections.
Exceptions
- ArgumentNullException
Thrown when a null is passed for the process provider.
Properties
AreConnectionPipesDisposed
Gets or sets a value indicating whether the local copies of pipe handles have been disposed.
protected bool AreConnectionPipesDisposed { get; set; }
Property Value
ConnectionKind
Gets a value indicating the type of data transport used by this connection, in this case, pipes.
public override ConnectionKind ConnectionKind { get; }
Property Value
IsConnectionOpen
Gets a value indicating whether the pipes to the external process are open.
protected override bool IsConnectionOpen { get; }
Property Value
Remarks
The returned value is a point-in-time snapshot. Because the pipe server process is owned by an external caller through IPipeServerProcessProvider, the process may exit or be disposed between this check and any subsequent I/O call. If the owning process has already been disposed, this property returns false rather than propagating the resulting InvalidOperationException. Transient races where the process exits after IsActive returns true are surfaced by SendDataAsync(ReadOnlyMemory<byte>, CancellationToken) as WebDriverBiDiConnectionException.
ReadPipeHandle
Gets the handle the external process reads from, through which this connection sends it data.
public string ReadPipeHandle { get; }
Property Value
Remarks
The names of the two handles take the external process's point of view, as the arguments of a browser's
pipe-based remote debugging option do (Chromium's --remote-debugging-io-pipes=<read>,<write>).
Returns an empty string once the connection has started: the first start disposes this process's local copy of the client handle, which the external process has inherited by then. Read the handle before starting the connection.
WritePipeHandle
Gets the handle the external process writes to, through which this connection receives its data.
public string WritePipeHandle { get; }
Property Value
Remarks
The names of the two handles take the external process's point of view; see ReadPipeHandle.
Returns an empty string once the connection has started: the first start disposes this process's local copy of the client handle, which the external process has inherited by then. Read the handle before starting the connection.
Methods
DisposeAsyncCore()
Asynchronously releases the resources used by this Connection.
protected override ValueTask DisposeAsyncCore()
Returns
- ValueTask
A task that represents the asynchronous dispose operation.
Remarks
Special note: We don't dispose the external process here, as it's owned by the caller and may be used across multiple connection sessions. Disposing it here could cause ObjectDisposedException in the caller if they attempt to use the process after the connection is disposed.
ReadPipeDataAsync(byte[], int, int, CancellationToken)
Asynchronously reads data from the underlying pipe of this connection.
protected virtual Task<int> ReadPipeDataAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
Parameters
bufferbyte[]The buffer to read data into.
offsetintThe offset in the buffer to start reading into.
countintThe maximum number of bytes to read.
cancellationTokenCancellationTokenA cancellation token used to propagate notification that the operation should be canceled.
Returns
- Task<int>
A task representing the asynchronous operation, with a result containing the number of bytes read.
ReceiveDataAsync()
Asynchronously receives data from the remote end of this connection. Messages are expected to be null-terminated as per the WebDriver BiDi pipe protocol.
protected override Task ReceiveDataAsync()
Returns
- Task
The task object representing the asynchronous operation.
SendConnectionDataAsync(ReadOnlyMemory<byte>, CancellationToken)
Asynchronously sends data to the underlying pipe of this connection.
protected override Task SendConnectionDataAsync(ReadOnlyMemory<byte> messageBuffer, CancellationToken cancellationToken = default)
Parameters
messageBufferReadOnlyMemory<byte>The buffer containing the data to be sent to the remote end of this connection via the pipe.
cancellationTokenCancellationTokenA cancellation token used to propagate notification that the operation should be canceled.
Returns
- Task
The task object representing the asynchronous operation.
Exceptions
- WebDriverBiDiConnectionException
Thrown when an exception is encountered sending data to the pipe.
StartConnectionAsync(CancellationToken)
Asynchronously makes the pipes to the external process ready to carry a session.
protected override Task StartConnectionAsync(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenA cancellation token used to propagate notification that the operation should be canceled.
Returns
- Task
The task object representing the asynchronous operation.
Remarks
A pipe connection accepts any connection string -- the pipes it uses are its own, and the string
only names the session -- so it does not override
ResolveConnectionString(string), and there is nothing for this method to
interpret. The pipes themselves are created with this connection and inherited by the external
process, so there is no connect operation to perform and nothing here is cancellable;
cancellationToken has already been observed by
StartAsync(string, CancellationToken) before this method is called.
Exceptions
- WebDriverBiDiConnectionException
Thrown when the external process has not been set, or is not running.
StopConnectionAsync(CancellationToken)
Marks the pipe connection as no longer carrying a session.
protected override Task StopConnectionAsync(CancellationToken cancellationToken)
Parameters
cancellationTokenCancellationTokenA cancellation token used to propagate notification that the operation should be canceled.
Returns
- Task
The task object representing the asynchronous operation.
Remarks
A pipe connection has no shutdown exchange with the remote end: the pipes are torn down by cancelling the connection, which StopAsync(CancellationToken) does after this method returns. All this method does is record that the session is over, before the receive loop is canceled, so that nothing sees the connection as active while it unwinds.
Cancelling the connection's token does not guarantee that an in-progress pipe read unblocks promptly on every supported target framework, so the wait for the receive loop that StopAsync(CancellationToken) performs is more often reached here than it is for a WebSocketConnection. Any data an abandoned read eventually returns is discarded rather than dispatched.
WritePipeDataAsync(ReadOnlyMemory<byte>, CancellationToken)
Asynchronously writes data to the underlying pipe of this connection.
protected virtual Task WritePipeDataAsync(ReadOnlyMemory<byte> messageBuffer, CancellationToken cancellationToken = default)
Parameters
messageBufferReadOnlyMemory<byte>The data to write to the pipe.
cancellationTokenCancellationTokenA cancellation token used to propagate notification that the operation should be canceled.
Returns
- Task
A task representing the asynchronous operation.
Remarks
The message and the null terminator that frames it are written as a single operation, so that cancellation cannot separate them. Written as two cancellable writes, a token canceled after the first one completes would leave an unterminated message in the pipe; the remote end would then read that message and the next one as a single malformed message, and every message after it would be framed one boundary out of step. Nothing later in the session can repair that, and the caller receives only the cancellation, so the corruption would surface as unrelated failures.
The only cancellation that reaches this method is the stopping of the connection; a caller's cancellation is honored only before the send begins (see SendDataAsync(ReadOnlyMemory<byte>, CancellationToken)). Even stopping the connection is honored only up to the point the first byte is written, and not after it: the flush that follows the write uses None, because by then the bytes are already committed to the stream.
WriteToPipeAsync(byte[], int, int, CancellationToken)
Asynchronously writes bytes to the underlying pipe of this connection.
protected virtual Task WriteToPipeAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken = default)
Parameters
bufferbyte[]The buffer containing the bytes to write.
offsetintThe offset in the buffer at which the bytes to write begin.
countintThe number of bytes to write.
cancellationTokenCancellationTokenA cancellation token used to propagate notification that the operation should be canceled.
Returns
- Task
A task representing the asynchronous operation.
Remarks
This is the counterpart of ReadPipeDataAsync(byte[], int, int, CancellationToken) for the outbound direction, and is the single point at which bytes reach the pipe. WritePipeDataAsync(ReadOnlyMemory<byte>, CancellationToken) calls it exactly once per message, with the message and its null terminator already assembled into one buffer, which is what keeps cancellation from splitting a frame.