UNPKG

sip.js

Version:

A SIP library for JavaScript

160 lines (159 loc) 6.86 kB
import { Transport as CoreTransport } from "../core/transport.js"; import { Emitter } from "./emitter.js"; import { TransportState } from "./transport-state.js"; /** * Transport layer interface expected by the `UserAgent`. * * @remarks * The transport behaves in a deterministic manner according to the * the state defined in {@link TransportState}. * * The "Connecting" state is ONLY entered in response to the user calling `connect()`. * The "Disconnecting" state is ONLY entered in response to the user calling `disconnect()`. * The `onConnect` callback is ALWAYS called upon transitioning to the "Connected" state. * The `onDisconnect` callback is ALWAYS called upon transitioning from the "Connected" state. * * Adherence to the state machine by the transport implementation is critical as the * UserAgent depends on this behavior. Furthermore it is critical that the transport * transition to the "Disconnected" state in all instances where network connectivity * is lost as the UserAgent, API, and application layer more generally depend on knowing * network was lost. For example, from a practical standpoint registrations and subscriptions are invalidated * when network is lost - particularly in the case of connection oriented transport * protocols such as a secure WebSocket transport. * * Proper handling the application level protocol recovery must be left to the application layer, * thus the transport MUST NOT attempt to "auto-recover" from or otherwise hide loss of network. * Note that callbacks and emitters such as `onConnect` and `onDisconnect` MUST NOT call methods * `connect()` and `direct()` synchronously (state change handlers must not loop back). They may * however do so asynchronously using a Promise resolution, `setTimeout`, or some other method. * For example... * ```ts * transport.onDisconnect = () => { * Promise.resolve().then(() => transport.connect()); * } * ``` * @public */ export interface Transport extends CoreTransport { /** * Transport state. * * @remarks * The initial Transport state MUST be "disconnected" (after calling constructor). */ readonly state: TransportState; /** * Transport state change emitter. */ readonly stateChange: Emitter<TransportState>; /** * Callback on state transition to "Connected". * * @remarks * When the `UserAgent` is constructed, this property is set. * ```txt * - The `state` MUST be "Connected" when called. * ``` */ onConnect: (() => void) | undefined; /** * Callback on state transition from "Connected". * * @remarks * When the `UserAgent` is constructed, this property is set. * ```txt * - The `state` MUST NOT "Connected" when called. * - If prior `state` is "Connecting" or "Connected", `error` MUST be defined. * - If prior `state` is "Disconnecting", `error` MUST NOT be undefined. * ``` * If the transition from "Connected" occurs because the transport * user requested it by calling `disconnect`, then `error` will be undefined. * Otherwise `error` will be defined to provide an indication that the * transport initiated the transition from "Connected" - for example, * perhaps network connectivity was lost. */ onDisconnect: ((error?: Error) => void) | undefined; /** * Callback on receipt of a message. * * @remarks * When the `UserAgent` is constructed, this property is set. * The `state` MUST be "Connected" when this is called. */ onMessage: ((message: string) => void) | undefined; /** * Connect to network. * * @remarks * ```txt * - If `state` is "Connecting", `state` MUST NOT transition before returning. * - If `state` is "Connected", `state` MUST NOT transition before returning. * - If `state` is "Disconnecting", `state` MUST transition to "Connecting" before returning. * - If `state` is "Disconnected" `state` MUST transition to "Connecting" before returning. * - The `state` MUST transition to "Connected" before resolving (assuming `state` is not already "Connected"). * - The `state` MUST transition to "Disconnecting" or "Disconnected" before rejecting and MUST reject with an Error. * ``` * Resolves when the transport connects. Rejects if transport fails to connect. * Rejects with {@link StateTransitionError} if a loop is detected. * In particular, callbacks and emitters MUST NOT call this method synchronously. */ connect(): Promise<void>; /** * Disconnect from network. * * @remarks * ```txt * - If `state` is "Connecting", `state` MUST transition to "Disconnecting" before returning. * - If `state` is "Connected", `state` MUST transition to "Disconnecting" before returning. * - If `state` is "Disconnecting", `state` MUST NOT transition before returning. * - If `state` is "Disconnected", `state` MUST NOT transition before returning. * - The `state` MUST transition to "Disconnected" before resolving (assuming `state` is not already "Disconnected"). * - The `state` MUST transition to "Connecting" or "Connected" before rejecting and MUST reject with an Error. * ``` * Resolves when the transport disconnects. Rejects if transport fails to disconnect. * Rejects with {@link StateTransitionError} if a loop is detected. * In particular, callbacks and emitters MUST NOT call this method synchronously. */ disconnect(): Promise<void>; /** * Dispose. * * @remarks * When the `UserAgent` is disposed or stopped, this method is called. * The `UserAgent` MUST NOT continue to utilize the instance after calling this method. */ dispose(): Promise<void>; /** * Returns true if the `state` equals "Connected". * * @remarks * This is equivalent to `state === TransportState.Connected`. * It is convenient. A common paradigm is, for example... * * @example * ```ts * // Monitor transport connectivity * userAgent.transport.stateChange.addListener(() => { * if (userAgent.transport.isConnected()) { * // handle transport connect * } else { * // handle transport disconnect * } * }); * ``` */ isConnected(): boolean; /** * Send a message. * * @remarks * ```txt * - If `state` is "Connecting", rejects with an Error. * - If `state` is "Connected", resolves when the message is sent otherwise rejects with an Error. * - If `state` is "Disconnecting", rejects with an Error. * - If `state` is "Disconnected", rejects with an Error. * ``` * @param message - Message to send. */ send(message: string): Promise<void>; }