Table of Contents

Class Transport

Namespace
WebDriverBiDi.Protocol
Assembly
WebDriverBiDi.dll

The transport object used for serializing and deserializing JSON data used in the WebDriver BiDi protocol. It uses a Connection object to communicate with the remote end, and does no further processing of the objects serialized or deserialized. Consumers of this class are expected to handle things like awaiting the response of a WebDriver BiDi command message.

public class Transport : IAsyncDisposable, ITransportConfiguration, ITransportDiagnostics
Inheritance
Transport
Implements
Inherited Members

Remarks

Message Queue Architecture: This transport uses an unbounded Channel<T> for message processing. Messages received from the connection are queued and processed sequentially by a dedicated reader task.

Memory Considerations: The unbounded queue means there is no limit on the number of messages that can be buffered if they arrive faster than they can be processed. In typical usage, message processing is fast enough that queue depth remains minimal. However, in high-throughput scenarios (e.g., thousands of rapid events or very slow event handlers), memory consumption could grow significantly.

Performance Characteristics: The single-reader, single-writer queue design provides optimal throughput for the typical case where messages arrive sequentially and are processed quickly. Event handlers that perform slow operations (I/O, CPU-intensive work) should use RunHandlerAsynchronously to avoid blocking the message processing thread.

Monitoring Queue Behavior: Use IncomingQueueDepth to monitor the number of messages buffered in the queue. A persistently growing value indicates that event handlers are not keeping up with the incoming message rate. If you suspect message backlog issues, consider:

  • Using RunHandlerAsynchronously for all event handlers that perform I/O or take more than a few milliseconds
  • Reducing the frequency of subscribed events if not all are needed
  • Implementing throttling or filtering logic in your event handlers

Thread Safety: This class is thread-safe. SendCommandAsync(CommandParameters, CancellationToken) may be called concurrently from multiple threads. Connection lifecycle operations (ConnectAsync(string, CancellationToken), DisconnectAsync(CancellationToken)) are serialized via an internal semaphore. Both DisconnectAsync(CancellationToken) and SendCommandAsync(CommandParameters, CancellationToken) check the connected state before taking the semaphore (and again after acquiring it). This prevents a deadlock when a disconnect is triggered while another caller already holds the lock, and lets a command sent from a synchronous event handler while DisconnectAsync(CancellationToken) is in progress fail immediately with WebDriverBiDiConnectionException rather than blocking on the semaphore for the duration of the shutdown wait. Event observers may be added or removed concurrently with message processing.

Constructors

Transport()

Initializes a new instance of the Transport class.

public Transport()

Transport(Connection)

Initializes a new instance of the Transport class with a given command timeout and connection.

public Transport(Connection connection)

Parameters

connection Connection

The Connection used to communicate with the protocol remote end.

Exceptions

ArgumentNullException

Thrown when a null is passed for the connection.

ArgumentException

Thrown when the provided Connection already has an observer for its OnDataReceived event.

Fields

LoggerComponentName

Gets the component name for this class to use in log messages.

public const string LoggerComponentName = "Transport"

Field Value

string

Properties

Connection

Gets the connection used to communicate with the browser.

protected Connection Connection { get; }

Property Value

Connection

ConnectionLockTimeout

Gets or sets the timeout to wait for exclusive access to this transport's connection while another operation holds it. The default is 60 seconds.

public TimeSpan ConnectionLockTimeout { get; set; }

Property Value

TimeSpan

Remarks

ConnectAsync(string, CancellationToken), DisconnectAsync(CancellationToken), SendCommandAsync(CommandParameters, CancellationToken) and RegisterTypeInfoResolverAsync(IJsonTypeInfoResolver, CancellationToken) each take exclusive access for the duration of their work, so one of them waits while another is in progress. Bounding that wait keeps an operation issued from code this transport itself invoked while holding the access — a synchronous observer of OnLogMessage that sends a command, for example — from waiting on an operation that is itself waiting on the observer to return. Such an operation fails with WebDriverBiDiTimeoutException instead of never completing. An observer that needs to drive the transport should be registered with RunHandlerAsynchronously so that it does not hold up the operation it was dispatched from.

The default is deliberately longer than the longest legitimate hold, so that lowering it is a deliberate choice rather than a trap. A disconnect that exhausts every wait it is allowed holds the access for the connection's close handshake and for its receive-loop wait (each bounded by ShutdownTimeout), and then for the message-queue drain (bounded by ShutdownTimeout), which is about 30 seconds at the default settings. A value shorter than the longest hold a session can legitimately take will fail operations that would otherwise have succeeded. Zero never waits, and InfiniteTimeSpan restores an unbounded wait.

Disposal takes the access too, so that an operation holding it -- a connect attempt, a disconnect, or the teardown after a connection loss -- is not torn down from under itself. That wait is bounded by ShutdownTimeout and by this timeout alike; whichever elapses first ends it, a warning is raised on OnLogMessage, and disposal proceeds.

Handling a lost connection waits for the access too, on the connection's receive loop. A wait abandoned there leaves the session standing rather than tearing it down without the access: the transport stays Connected, its pending commands end at their own timeouts, and a Warn message naming the cause is raised on OnLogMessage.

Exceptions

ArgumentOutOfRangeException

Thrown when the value is negative (other than InfiniteTimeSpan) or exceeds the maximum timer duration supported by the runtime.

EventHandlerExceptionBehavior

Gets or sets a value indicating how this Transport should behave when an observer of an observable event throws, or the task it returns faults. Defaults to Ignore, in which case the error is neither collected nor thrown from a later call; it is still raised on OnEventHandlerErrorOccurred and as the EventHandlerError event of WebDriverBiDiEventSource.

public TransportErrorBehavior EventHandlerExceptionBehavior { get; set; }

Property Value

TransportErrorBehavior

IncomingQueueDepth

Gets the number of messages currently buffered in the incoming message queue and waiting to be processed by the reader task.

public virtual int IncomingQueueDepth { get; }

Property Value

int

Remarks

This property surfaces the depth of the unbounded Channel<T> described in the class-level remarks. It is intended for diagnostics and observability: a persistently growing value indicates that event handlers are not keeping up with the incoming message rate, and should prompt investigation of handler duration or the use of RunHandlerAsynchronously.

The value reflects the queue for the current connection. Each call to ConnectAsync(string, CancellationToken) installs a fresh queue whose depth begins at zero, and every message is counted against the queue it was written to for as long as that queue is being drained. A reconnect that gives up waiting for the previous connection's reader therefore reports only the current connection's backlog, even while the previous reader is still draining what remains of its own queue. Reading this property before ConnectAsync(string, CancellationToken) has ever been called, or after DisconnectAsync(CancellationToken), returns the depth of the remaining (possibly drained) queue rather than throwing.

Thread Safety: This property is safe to read concurrently with message production and consumption. The returned value is a snapshot and may be stale by the time the caller observes it.

LastCommandId

Gets the ID of the last command to be added.

protected long LastCommandId { get; }

Property Value

long

Remarks

This method uses Interlocked.Read for downlevel compatibility with legacy and 32-bit framework implementations.

LogLevel

Gets or sets the minimum WebDriverBiDiLogLevel at which log messages are raised. Defaults to Info.

public virtual WebDriverBiDiLogLevel LogLevel { get; set; }

Property Value

WebDriverBiDiLogLevel

Remarks

By default this is the same setting as LogLevel on the connection this transport wraps, which holds it for the whole pipeline; setting it here sets it for the connection's messages as well as this transport's own. Raise it to Debug for per-command messages, or to Trace to also see the raw protocol traffic the connection logs.

A derived transport may override this to keep a level of its own rather than share the connection's. Note what that decouples: IsLogLevelEnabled(WebDriverBiDiLogLevel) and this transport's LogAsync read this property, and so does the driver through TransportConfiguration, so all three follow the override; the connection keeps filtering its own messages — the SEND and RECV traffic among them — by LogLevel. An override whose setter also assigns LogLevel keeps the whole pipeline together.

MaxTrackedCanceledCommands

Gets or sets the number of most recent command cancellations within which a canceled command is remembered, so that a response arriving for it later is recognized and discarded. The default is DefaultMaxTrackedCanceledCommands.

public uint MaxTrackedCanceledCommands { get; set; }

Property Value

uint

Remarks

A command that times out, or is canceled, may still be answered by the remote end. A response for a command still remembered is discarded quietly; a response for one that has been forgotten, because this many further commands were canceled after it, is treated as an unknown message or an unexpected error, as UnknownMessageBehavior and UnexpectedErrorBehavior direct. Raise the value if a session cancels many commands whose responses may arrive long afterwards. A value of zero disables the tracking.

Canceled commands are remembered per session, so the value takes effect for the session started by the next ConnectAsync(string, CancellationToken), and does not change the session in progress.

OnConnectionLost

Gets an observable event that notifies when an established connection ends without the client stopping it: the remote end closed it, as when the browser exits, or it failed.

public ObservableEvent<ConnectionLostEventArgs> OnConnectionLost { get; }

Property Value

ObservableEvent<ConnectionLostEventArgs>

Remarks

It is raised by the loop that dispatches the session's messages, after every message received before the loss, once the transport has torn the session down: its state is Disconnected and commands in flight have failed. Like any observer run on that loop, an observer can stop the transport, but connecting it again waits for the loop to finish, at most ShutdownTimeout, so reconnect from outside the observer, or from one added with RunHandlerAsynchronously. It is not raised when the client stops the transport, nor for a connection lost while it is being established, which fails the start instead.

OnEventHandlerErrorOccurred

Gets an observable event that notifies when an error occurs in an observer of an observable event.

public ObservableEvent<EventHandlerErrorOccurredEventArgs> OnEventHandlerErrorOccurred { get; }

Property Value

ObservableEvent<EventHandlerErrorOccurredEventArgs>

OnEventReceived

Gets an observable event that notifies when an event is received from the protocol.

public ObservableEvent<EventReceivedEventArgs> OnEventReceived { get; }

Property Value

ObservableEvent<EventReceivedEventArgs>

OnLogMessage

Gets an observable event that notifies when a log message is written.

public ObservableEvent<LogMessageEventArgs> OnLogMessage { get; }

Property Value

ObservableEvent<LogMessageEventArgs>

OnUnexpectedErrorReceived

Gets an observable event that notifies when an error is received from the protocol that is not the result of a command execution.

public ObservableEvent<ErrorReceivedEventArgs> OnUnexpectedErrorReceived { get; }

Property Value

ObservableEvent<ErrorReceivedEventArgs>

OnUnknownMessageReceived

Gets an observable event that notifies when an unknown message is received from the protocol.

public ObservableEvent<UnknownMessageReceivedEventArgs> OnUnknownMessageReceived { get; }

Property Value

ObservableEvent<UnknownMessageReceivedEventArgs>

PendingCommandCount

Gets the number of commands that have been sent to the remote end and are awaiting a response.

public virtual int PendingCommandCount { get; }

Property Value

int

Remarks

This property is intended for diagnostics and observability alongside IncomingQueueDepth. A persistently high value suggests that the remote end is not responding promptly, or that a burst of commands is in flight without corresponding responses yet.

Reading this property before ConnectAsync(string, CancellationToken) has ever been called, or after DisconnectAsync(CancellationToken), returns the count of the pending-command collection in its current state rather than throwing. The collection is cleared during DisconnectAsync(CancellationToken), so reads after a disconnect typically return zero.

Thread Safety: This property is safe to read concurrently with command send and response processing. The returned value is a snapshot and may be stale by the time the caller observes it.

PendingCommands

Gets the collection of pending commands of the current session: commands that have been sent and have not yet received a response. This collection is thread-safe.

protected PendingCommandCollection PendingCommands { get; }

Property Value

PendingCommandCollection

Remarks

Each session has its own collection, created by ConnectAsync(string, CancellationToken) with the capacity set by MaxTrackedCanceledCommands, so the collection returned here changes when the transport reconnects.

ProtocolErrorBehavior

Gets or sets a value indicating how this Transport should behave when a protocol error is encountered: a message recognized as an error response or as a registered event whose payload cannot be deserialized, or an unexpected failure while processing an incoming message. An error response whose ID matches a pending command is not a protocol error; it fails that command. A message that cannot be parsed as JSON at all is an unknown message (see UnknownMessageBehavior). Defaults to Ignore, in which case the error is neither collected nor thrown from a later call; it is still written to OnLogMessage at Error and, for a payload that cannot be deserialized, raised as the ProtocolError event of WebDriverBiDiEventSource as well. A fault of the message-processing loop itself raises that EventSource event and is not logged. No observable event is raised for any of them.

public TransportErrorBehavior ProtocolErrorBehavior { get; set; }

Property Value

TransportErrorBehavior

ShutdownTimeout

Gets or sets the timeout to wait for message processing to complete during shutdown. If message processing does not complete within this timeout, the shutdown stops waiting and proceeds, and any pending commands are canceled. The default is 10 seconds.

public TimeSpan ShutdownTimeout { get; set; }

Property Value

TimeSpan

Remarks

This timeout applies to waiting for the incoming message queue to empty, to waiting for the messages in the queue to be processed, and, during disposal, to waiting for an in-flight connect attempt to complete before the transport's resources are released.

Abandoning the wait does not stop the reader. Messages already delivered to the queue go on being processed in the background, and their handlers go on running; what this timeout bounds is how long the shutdown waits for them, not whether they run. A subsequent ConnectAsync(string, CancellationToken) waits for that processing to finish, bounded by this same timeout, before opening a new connection.

Exceptions

ArgumentOutOfRangeException

Thrown when the value is negative (other than InfiniteTimeSpan) or exceeds the maximum timer duration supported by the runtime.

State

Gets a value indicating the lifecycle state of this transport with respect to its connection to a remote end. The returned value is a snapshot and may be stale by the time the caller observes it.

public TransportState State { get; }

Property Value

TransportState

Remarks

Every state transition is performed while holding the connection lock (or, in the case of a connection-loss transition, while racing that lock for ownership), so the setter is an unconditional atomic publish; no compare-and-swap is required.

TimeProvider

Gets or sets the TimeProvider whose clock measures this transport's ShutdownTimeout waits. Defaults to System. A derived type may substitute another, for example to drive the waits with virtual time in a test, in the same way that ObservableEvent<T> exposes its provider to derived types. The Connection measures its own timeouts with its own provider.

protected TimeProvider TimeProvider { get; set; }

Property Value

TimeProvider

UnexpectedErrorBehavior

Gets or sets a value indicating how this Transport should behave when an unexpected error is encountered, meaning an error response received with no corresponding command. Defaults to Ignore, in which case the error is neither collected nor thrown from a later call; it is still raised on OnUnexpectedErrorReceived. An error response for a command that has timed out or been canceled is not an unexpected error; it is recognized, logged, and discarded (see CancelCommand(Command, CommandCancellationReason)).

public TransportErrorBehavior UnexpectedErrorBehavior { get; set; }

Property Value

TransportErrorBehavior

UnhandledErrors

Gets the collection of unhandled errors captured by this transport. This collection is thread-safe. Use this collection to inspect unhandled errors that have been captured, and to clear captured errors if desired.

protected UnhandledErrorCollection UnhandledErrors { get; }

Property Value

UnhandledErrorCollection

UnknownMessageBehavior

Gets or sets a value indicating how this Transport should behave when an unknown message is encountered: a message that cannot be parsed as JSON, or one that is not a command response, an error response, or an event registered with this transport, such as a response for a command ID that was never issued or an event whose name is not registered. Defaults to Ignore, in which case the error is neither collected nor thrown from a later call; it is still raised on OnUnknownMessageReceived and as the UnknownMessageReceived event of WebDriverBiDiEventSource, and a message that cannot be parsed is also written to OnLogMessage at Error. A response for a command that has timed out or been canceled is not an unknown message; it is recognized, logged, and discarded (see CancelCommand(Command, CommandCancellationReason)).

public TransportErrorBehavior UnknownMessageBehavior { get; set; }

Property Value

TransportErrorBehavior

Methods

AcquireConnectionLockAsync(CancellationToken)

Asynchronously acquires the connection lock to ensure thread-safe operations for connection-related actions.

protected virtual Task AcquireConnectionLockAsync(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

A cancellation token that can be used to cancel the asynchronous operation.

Returns

Task

A task that represents the asynchronous acquire operation.

Remarks

The uncontended case is the overwhelmingly common one, and it is taken without allocating: the timeout machinery is built only once the lock is found to be held. The bound is measured on this transport's TimeProvider, so a transport running on virtual time waits on that same clock.

Bounding the wait is what keeps a re-entrant call from hanging forever. This transport dispatches observers while holding the lock — the connection raises its own traffic message from inside SendDataAsync(ReadOnlyMemory<byte>, CancellationToken), and both the connection and this transport log around connect and disconnect — so a synchronous observer that calls back into the transport arrives here for a lock its own caller holds. It now fails with a WebDriverBiDiTimeoutException naming the cause, and the operation it interrupted proceeds, rather than the two waiting on each other indefinitely.

Exceptions

WebDriverBiDiTimeoutException

Thrown when the lock is not acquired within ConnectionLockTimeout.

OperationCanceledException

Thrown when cancellationToken is canceled.

AddEventMessageType(string, Type)

Adds an event message type to the map of known event message types. To intercept or rewrite registrations, override RegisterEventMessage<T>(string), which the driver calls.

protected void AddEventMessageType(string eventName, Type eventMessageType)

Parameters

eventName string

The name of the event.

eventMessageType Type

The type of data to be returned in the event.

Remarks

The type is deserialized through the serializer's metadata for it, because the envelope converter needs the payload type as a compile-time argument and building it for a runtime Type would need reflection, which native AOT cannot keep. Envelopes registered this way are therefore read more leniently than those registered through RegisterEventMessage<T>(string): a missing method or a null params is accepted here, and surfaces later as a protocol error rather than as a deserialization failure. Register through RegisterEventMessage<T>(string) wherever the payload type is known at compile time.

CancelCommand(Command, CommandCancellationReason)

Cancels a pending command and removes it from the pending command collection. If the command has already completed or been removed, this method is a safe no-op.

public virtual bool CancelCommand(Command command, CommandCancellationReason reason = CommandCancellationReason.Canceled)

Parameters

command Command

The command to cancel.

reason CommandCancellationReason

The reason the command is being canceled.

Returns

bool

true if the cancellation took effect; false if the command had already completed with a result or fault, in which case that outcome stands.

Remarks

The remote end does not know that the local end has stopped waiting, so it may still send a response for the canceled command. The command is remembered (see CancelPendingCommand(Command, CommandCancellationReason)) so that such a response is recognized and discarded rather than being reported as an unknown message or an unexpected error.

CaptureUnhandledError(UnhandledErrorKind, Exception, string)

Captures an unhandled error in the protocol.

protected virtual void CaptureUnhandledError(UnhandledErrorKind errorType, Exception ex, string terminalReason)

Parameters

errorType UnhandledErrorKind

The UnhandledErrorKind describing the type of error.

ex Exception

The exception thrown for the error.

terminalReason string

The reason for terminating the session, if the error is a terminal error.

Remarks

This method is protected virtual to allow test doubles to precisely determine when an unhandled error has been captured and propagated to the unhandled errors collection.

ConnectAsync(string, CancellationToken)

Asynchronously connects to the remote end web socket.

public virtual Task ConnectAsync(string connectionString, CancellationToken cancellationToken = default)

Parameters

connectionString string

The URI used to connect to the web socket.

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

Connecting starts a new session and clears the errors the previous session accumulated under Collect. Those errors are thrown only by DisconnectAsync(CancellationToken). After a remote disconnect the transport is already in the Disconnected state, so this method proceeds; to observe the errors collected up to the disconnect, call DisconnectAsync(CancellationToken) (which returns promptly and throws them) before reconnecting. Reconnecting directly discards them.

A Connection that is already open is adopted rather than opened again, so a caller who opened the connection themselves before handing it to this transport may start a session over it. Because nothing is opened in that case, connectionString cannot select a different remote end, and one that names a different remote end is rejected rather than silently ignored: pass the value the connection was opened with, or stop the connection first and let this method open it. The two values are compared exactly, so a string that names the same remote end in a different form (a different case, or a trailing slash) is treated as a different one. A connection that is not open is unaffected by any of this, including one being reopened to a different remote end after DisconnectAsync(CancellationToken).

Exceptions

WebDriverBiDiConnectionException

Thrown when the transport is already connected to a remote end, when the Connection refuses to open, or when the connection is lost while the session is being established. The last case carries the loss the connection reported as its inner exception; the transport is left disconnected, so a further attempt may be made.

WebDriverBiDiTimeoutException

Propagated from StartAsync(string, CancellationToken) when the connection is not established within its StartupTimeout.

ArgumentException

Propagated from StartAsync(string, CancellationToken) when connectionString is not acceptable to the connection, or when the Connection is already connected with a ConnectionString different than connectionString. WebSocketConnection throws this when the value is not a valid absolute URI, or when its scheme is neither ws nor wss.

OperationCanceledException

Thrown when cancellationToken is canceled.

ObjectDisposedException

Thrown when the transport is disposed, including when disposal begins while this attempt is waiting for the connection lock or is connecting; the attempt then fails rather than connecting a transport that is being disposed.

CreateCommand(CommandParameters)

Creates a Command object from the specified command parameters.

protected virtual Command CreateCommand(CommandParameters commandData)

Parameters

commandData CommandParameters

The CommandParameters object containing the command data.

Returns

Command

The created Command.

Remarks

This method allows a developer to override the creation of a command. This is useful for cases where additional properties need to be sent in the command envelope as opposed to the command parameters object.

CreateIncomingMessage(IMemoryOwner<byte>, int)

Creates an IncomingMessage object for the data received by this Transport.

protected virtual IncomingMessage CreateIncomingMessage(IMemoryOwner<byte> owner, int length)

Parameters

owner IMemoryOwner<byte>

The IMemoryOwner<T> whose buffer contains the incoming message data. Ownership transfers to the returned IncomingMessage, which will dispose it on disposal.

length int

The length, in bytes, of the incoming message within the owner's buffer.

Returns

IncomingMessage

The IncomingMessage object for the data received.

DisconnectAsync(bool, CancellationToken)

Asynchronously disconnects from the remote end web socket.

protected virtual Task DisconnectAsync(bool throwCollectedExceptions, CancellationToken cancellationToken = default)

Parameters

throwCollectedExceptions bool

A value indicating whether to throw the collected exceptions.

cancellationToken CancellationToken

A cancellation token used to propagate notification that the operation should be canceled.

Returns

Task

The task object representing the asynchronous operation.

DisconnectAsync(CancellationToken)

Asynchronously disconnects from the remote end web socket.

public virtual Task DisconnectAsync(CancellationToken cancellationToken = default)

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.

Exceptions

AggregateException

Thrown when Collect is configured for any error category and one or more errors were collected during the session. The aggregated exceptions describe the collected errors. They are thrown at most once per session, by whichever disconnect claims them.

WebDriverBiDiTimeoutException

Thrown when exclusive access to the connection is not obtained within ConnectionLockTimeout.

OperationCanceledException

Thrown when cancellationToken is canceled.

DisposeAsync()

Asynchronously releases the resources used by this Transport.

public ValueTask DisposeAsync()

Returns

ValueTask

A task that represents the asynchronous dispose operation.

Remarks

Disposing a transport that is already disposed does nothing, as IAsyncDisposable requires. The teardown cannot simply be repeated: it releases resources that are then gone, and a second run would again wait, up to ShutdownTimeout, for an operation still holding the connection lock, which is what remains when the first run's wait for that operation timed out.

DisposeAsyncCore()

Asynchronously releases the resources used by this Transport. Override this method in derived classes to add custom cleanup logic.

protected virtual ValueTask DisposeAsyncCore()

Returns

ValueTask

A task that represents the asynchronous dispose operation.

Remarks

DisposeAsync() calls this method once, on the first disposal only, and records the disposal before calling it, so an override neither repeats that guard nor needs one of its own. Because the transport is marked disposed before the teardown rather than after it, an operation that rejects a disposed transport -- ConnectAsync(string, CancellationToken), SendCommandAsync(CommandParameters, CancellationToken) and RegisterTypeInfoResolverAsync(IJsonTypeInfoResolver, CancellationToken) -- fails from the moment disposal begins rather than only once it has finished.

The teardown first waits, up to ShutdownTimeout, for any operation holding the connection lock to finish: a connect attempt, a disconnect, or the teardown after a connection loss. A connect attempt still in progress then fails with ObjectDisposedException rather than completing. If the wait times out, the teardown proceeds without it, and the operation still holding the lock finishes against the disposed transport.

GetNextCommandId()

Increments the command ID for the command to be sent.

protected long GetNextCommandId()

Returns

long

The command ID for the command to be sent.

Remarks

Command IDs are unique for the lifetime of the transport; they are not reset when the transport reconnects. A response still in flight from a previous session, such as one delivered late over a connection that survives the reconnect, therefore cannot be mistaken for the response to a command of the current session.

IsLogLevelEnabled(WebDriverBiDiLogLevel)

Gets a value indicating whether a message at the given level would be raised on OnLogMessage, so that a caller can avoid building a message that would be discarded.

public bool IsLogLevelEnabled(WebDriverBiDiLogLevel level)

Parameters

level WebDriverBiDiLogLevel

The WebDriverBiDiLogLevel of the message the caller would raise.

Returns

bool

true if such a message would be raised; otherwise, false.

Remarks

The transport uses this for its per-command Debug messages, each of which composes a string naming the command; a custom transport should use it for the same purpose. Off is never enabled. It selects "no messages at all" when assigned to LogLevel, and is not a level a message can carry; without the explicit test it would compare as enabled against every setting, because it is the highest value.

ProcessMessageAsync(IncomingMessage, PendingCommandCollection)

Processes a single incoming message read from the connection.

protected virtual Task ProcessMessageAsync(IncomingMessage packet, PendingCommandCollection pendingCommands)

Parameters

packet IncomingMessage

The incoming message to process.

pendingCommands PendingCommandCollection

The pending commands of the session on whose connection the message was received, against which a response in the message is resolved. After a reconnect, this is not the collection in PendingCommands for a message the previous session's reader is still processing.

Returns

Task

A Task representing the asynchronous operation.

Remarks

This method is protected virtual to allow test doubles to observe or delay the processing of individual messages (for example to create a backlog of pending messages while the transport disconnects).

ReadIncomingMessagesAsync()

Reads and processes messages from the incoming message queue until the queue is closed. This method is the body of the task started by ConnectAsync(string, CancellationToken).

protected virtual Task ReadIncomingMessagesAsync()

Returns

Task

A task representing the asynchronous message-processing loop.

Remarks

This method is protected virtual to allow test doubles to substitute the message-processing loop — for example, to simulate an unrecoverable fault on the outer await so that the fault continuation attached in ConnectAsync(string, CancellationToken) can be exercised.

RegisterEventMessage<T>(string)

Registers an event message to be recognized when received from the connection.

public virtual void RegisterEventMessage<T>(string eventName)

Parameters

eventName string

The name of the event.

Type Parameters

T

The type of data to be returned in the event.

RegisterTypeInfoResolverAsync(IJsonTypeInfoResolver, CancellationToken)

Registers an additional IJsonTypeInfoResolver for JSON serialization and deserialization. This allows custom types, such as those from user-defined modules, to be serialized in AOT scenarios where reflection-based serialization is unavailable. This method must be called while the transport is not connected: before the first connection, or after a disconnect. Resolvers registered earlier remain in effect.

public virtual Task RegisterTypeInfoResolverAsync(IJsonTypeInfoResolver resolver, CancellationToken cancellationToken = default)

Parameters

resolver IJsonTypeInfoResolver

The type info resolver to add.

cancellationToken CancellationToken

A cancellation token that can be used to cancel the asynchronous operation.

Returns

Task

A Task representing the asynchronous operation.

Exceptions

ArgumentNullException

Thrown when resolver is null.

ObjectDisposedException

Thrown when the transport has been disposed.

InvalidOperationException

Thrown if the transport is already connected to a remote end.

WebDriverBiDiTimeoutException

Thrown when exclusive access to the connection is not obtained within ConnectionLockTimeout.

OperationCanceledException

Thrown when cancellationToken is canceled.

ReleaseConnectionLock()

Releases the connection lock to allow other threads to perform connection-related actions.

protected virtual void ReleaseConnectionLock()

SendCommandAsync(CommandParameters, CancellationToken)

Asynchronously sends a command to the remote end.

public virtual Task<Command> SendCommandAsync(CommandParameters commandData, CancellationToken cancellationToken = default)

Parameters

commandData CommandParameters

The command settings object containing all data required to execute the command.

cancellationToken CancellationToken

A cancellation token used to propagate notification that the operation should be canceled.

Returns

Task<Command>

The task object representing the asynchronous operation.

Exceptions

WebDriverBiDiException

Thrown if the command ID is already in use.

WebDriverBiDiSerializationException

Thrown if the command parameters cannot be serialized to JSON: when an extension-data entry on the command or on any object inside its parameters uses a property name that object already serializes; when an object-typed member holds a value JSON cannot express, such as a non-finite double; or when a value's type has no serialization metadata. The failure that caused it is the InnerException.

WebDriverBiDiConnectionException

Thrown when the transport is not connected to a remote end.

WebDriverBiDiTimeoutException

Thrown when exclusive access to the connection is not obtained within ConnectionLockTimeout.

ArgumentNullException

Thrown when the command parameters are null.

OperationCanceledException

Thrown when cancellationToken is canceled.

SerializeCommand(Command)

Serializes a command for transmission across the WebSocket connection.

protected virtual byte[] SerializeCommand(Command command)

Parameters

command Command

The command to serialize.

Returns

byte[]

The UTF-8 encoded JSON bytes representing the command.