Table of Contents

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

processProvider IPipeServerProcessProvider

An 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

bool

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

ConnectionKind

IsConnectionOpen

Gets a value indicating whether the pipes to the external process are open.

protected override bool IsConnectionOpen { get; }

Property Value

bool

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

string

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

string

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

buffer byte[]

The buffer to read data into.

offset int

The offset in the buffer to start reading into.

count int

The maximum number of bytes to read.

cancellationToken CancellationToken

A 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

messageBuffer ReadOnlyMemory<byte>

The buffer containing the data to be sent to the remote end of this connection via the pipe.

cancellationToken CancellationToken

A 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

cancellationToken CancellationToken

A 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

cancellationToken CancellationToken

A 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

messageBuffer ReadOnlyMemory<byte>

The data to write to the pipe.

cancellationToken CancellationToken

A 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

buffer byte[]

The buffer containing the bytes to write.

offset int

The offset in the buffer at which the bytes to write begin.

count int

The number of bytes to write.

cancellationToken CancellationToken

A 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.