Skip to content

Commit

Permalink
feat: PeerError from connections
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasgloning committed Sep 2, 2023
1 parent d43efa7 commit ad3a0cb
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 73 deletions.
24 changes: 18 additions & 6 deletions lib/baseconnection.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { EventEmitter, type ValidEventTypes } from "eventemitter3";
import type { Peer } from "./peer";
import type { ServerMessage } from "./servermessage";
import type { ConnectionType } from "./enums";
import { BaseConnectionErrorType } from "./enums";
import {
EventEmitterWithError,
type EventsWithError,
PeerError,
} from "./peerError";
import type { ValidEventTypes } from "eventemitter3";

export type BaseConnectionEvents = {
export interface BaseConnectionEvents<
ErrorType extends string = BaseConnectionErrorType,
> extends EventsWithError<ErrorType> {
/**
* Emitted when either you or the remote peer closes the connection.
*
Expand All @@ -17,13 +25,17 @@ export type BaseConnectionEvents = {
* connection.on('error', (error) => { ... });
* ```
*/
error: (error: Error) => void;
error: (error: PeerError<`${ErrorType}`>) => void;
iceStateChanged: (state: RTCIceConnectionState) => void;
};
}

export abstract class BaseConnection<
T extends ValidEventTypes,
> extends EventEmitter<T & BaseConnectionEvents> {
SubClassEvents extends ValidEventTypes,
ErrorType extends string = never,
> extends EventEmitterWithError<
ErrorType | BaseConnectionErrorType,
SubClassEvents & BaseConnectionEvents<BaseConnectionErrorType | ErrorType>
> {
protected _open = false;

/**
Expand Down
7 changes: 5 additions & 2 deletions lib/dataconnection/BufferedConnection/Json.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BufferedConnection } from "./BufferedConnection";
import { SerializationType } from "../../enums";
import { DataConnectionErrorType, SerializationType } from "../../enums";
import { util } from "../../util";

export class Json extends BufferedConnection {
Expand Down Expand Up @@ -27,7 +27,10 @@ export class Json extends BufferedConnection {
override _send(data, _chunked) {
const encodedData = this.encoder.encode(this.stringify(data));
if (encodedData.byteLength >= util.chunkedMTU) {
this.emit("error", new Error("Message too big for JSON channel"));
this.emitError(
DataConnectionErrorType.MessageToBig,
"Message too big for JSON channel",
);
return;
}
this._bufferedSend(encodedData);
Expand Down
34 changes: 19 additions & 15 deletions lib/dataconnection/DataConnection.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
import logger from "../logger";
import { Negotiator } from "../negotiator";
import { ConnectionType, ServerMessageType } from "../enums";
import {
BaseConnectionErrorType,
ConnectionType,
DataConnectionErrorType,
ServerMessageType,
} from "../enums";
import type { Peer } from "../peer";
import { BaseConnection } from "../baseconnection";
import { BaseConnection, type BaseConnectionEvents } from "../baseconnection";
import type { ServerMessage } from "../servermessage";
import type { DataConnection as IDataConnection } from "./DataConnection";
import type { EventsWithError } from "../peerError";
import { randomToken } from "../utils/randomToken";

type DataConnectionEvents = {
export interface DataConnectionEvents
extends EventsWithError<DataConnectionErrorType | BaseConnectionErrorType>,
BaseConnectionEvents<DataConnectionErrorType | BaseConnectionErrorType> {
/**
* Emitted when data is received from the remote peer.
*/
Expand All @@ -16,23 +23,22 @@ type DataConnectionEvents = {
* Emitted when the connection is established and ready-to-use.
*/
open: () => void;
};
}

/**
* Wraps a DataChannel between two Peers.
*/
export abstract class DataConnection
extends BaseConnection<DataConnectionEvents>
implements IDataConnection
{
export abstract class DataConnection extends BaseConnection<
DataConnectionEvents,
DataConnectionErrorType
> {
protected static readonly ID_PREFIX = "dc_";
protected static readonly MAX_BUFFERED_AMOUNT = 8 * 1024 * 1024;

private _negotiator: Negotiator<DataConnectionEvents, this>;
abstract readonly serialization: string;
readonly reliable: boolean;

// public type: ConnectionType.Data;
public get type() {
return ConnectionType.Data;
}
Expand Down Expand Up @@ -123,11 +129,9 @@ export abstract class DataConnection
/** Allows user to send data. */
public send(data: any, chunked = false) {
if (!this.open) {
super.emit(
"error",
new Error(
"Connection is not open. You should listen for the `open` event before sending messages.",
),
this.emitError(
DataConnectionErrorType.NotOpenYet,
"Connection is not open. You should listen for the `open` event before sending messages.",
);
return;
}
Expand Down
10 changes: 10 additions & 0 deletions lib/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ export enum PeerErrorType {
WebRTC = "webrtc",
}

export enum BaseConnectionErrorType {
NegotiationFailed = "negotiation-failed",
ConnectionClosed = "connection-closed",
}

export enum DataConnectionErrorType {
NotOpenYet = "not-open-yet",
MessageToBig = "message-too-big",
}

export enum SerializationType {
Binary = "binary",
BinaryUTF8 = "binary-utf8",
Expand Down
11 changes: 3 additions & 8 deletions lib/exports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Peer } from "./peer";
import { CborPeer } from "./cborPeer";
import { MsgPackPeer } from "./msgPackPeer";

export type { PeerEvents, PeerError, PeerOptions } from "./peer";
export type { PeerEvents, PeerOptions } from "./peer";

export type {
PeerJSOption,
Expand All @@ -15,13 +15,7 @@ export type { UtilSupportsObj } from "./util";
export type { DataConnection } from "./dataconnection/DataConnection";
export type { MediaConnection } from "./mediaconnection";
export type { LogLevel } from "./logger";
export type {
ConnectionType,
PeerErrorType,
SerializationType,
SocketEventType,
ServerMessageType,
} from "./enums";
export * from "./enums";

export { BufferedConnection } from "./dataconnection/BufferedConnection/BufferedConnection";
export { StreamConnection } from "./dataconnection/StreamConnection/StreamConnection";
Expand All @@ -31,4 +25,5 @@ export type { SerializerMapping } from "./peer";

export { Peer, MsgPackPeer, CborPeer };

export { PeerError } from "./peerError";
export default Peer;
8 changes: 4 additions & 4 deletions lib/mediaconnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import logger from "./logger";
import { Negotiator } from "./negotiator";
import { ConnectionType, ServerMessageType } from "./enums";
import type { Peer } from "./peer";
import { BaseConnection } from "./baseconnection";
import { BaseConnection, type BaseConnectionEvents } from "./baseconnection";
import type { ServerMessage } from "./servermessage";
import type { AnswerOption } from "./optionInterfaces";

export type MediaConnectionEvents = {
export interface MediaConnectionEvents extends BaseConnectionEvents<never> {
/**
* Emitted when a connection to the PeerServer is established.
*
Expand All @@ -22,7 +22,7 @@ export type MediaConnectionEvents = {
* @beta
*/
willCloseOnRemote: () => void;
};
}

/**
* Wraps WebRTC's media streams.
Expand All @@ -32,7 +32,7 @@ export class MediaConnection extends BaseConnection<MediaConnectionEvents> {
private static readonly ID_PREFIX = "mc_";
readonly label: string;

private _negotiator: Negotiator<MediaConnectionEvents, MediaConnection>;
private _negotiator: Negotiator<MediaConnectionEvents, this>;
private _localStream: MediaStream;
private _remoteStream: MediaStream;

Expand Down
25 changes: 15 additions & 10 deletions lib/negotiator.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import logger from "./logger";
import type { MediaConnection } from "./mediaconnection";
import type { DataConnection } from "./dataconnection/DataConnection";
import { ConnectionType, PeerErrorType, ServerMessageType } from "./enums";
import {
BaseConnectionErrorType,
ConnectionType,
PeerErrorType,
ServerMessageType,
} from "./enums";
import type { BaseConnection, BaseConnectionEvents } from "./baseconnection";
import type { ValidEventTypes } from "eventemitter3";

/**
* Manages all negotiations between Peers.
*/
export class Negotiator<
A extends ValidEventTypes,
T extends BaseConnection<A | BaseConnectionEvents>,
Events extends ValidEventTypes,
ConnectionType extends BaseConnection<Events | BaseConnectionEvents>,
> {
constructor(readonly connection: T) {}
constructor(readonly connection: ConnectionType) {}

/** Returns a PeerConnection object set up correctly (for data, media). */
startConnection(options: any) {
Expand Down Expand Up @@ -88,19 +93,19 @@ export class Negotiator<
logger.log(
"iceConnectionState is failed, closing connections to " + peerId,
);
this.connection.emit(
"error",
new Error("Negotiation of connection to " + peerId + " failed."),
this.connection.emitError(
BaseConnectionErrorType.NegotiationFailed,
"Negotiation of connection to " + peerId + " failed.",
);
this.connection.close();
break;
case "closed":
logger.log(
"iceConnectionState is closed, closing connections to " + peerId,
);
this.connection.emit(
"error",
new Error("Connection to " + peerId + " closed."),
this.connection.emitError(
BaseConnectionErrorType.ConnectionClosed,
"Connection to " + peerId + " closed.",
);
this.connection.close();
break;
Expand Down
36 changes: 8 additions & 28 deletions lib/peer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { EventEmitter } from "eventemitter3";
import { util } from "./util";
import logger, { LogLevel } from "./logger";
import { Socket } from "./socket";
Expand All @@ -21,6 +20,8 @@ import { BinaryPack } from "./dataconnection/BufferedConnection/BinaryPack";
import { Raw } from "./dataconnection/BufferedConnection/Raw";
import { Json } from "./dataconnection/BufferedConnection/Json";

import { EventEmitterWithError, PeerError } from "./peerError";

class PeerOptions implements PeerJSOption {
/**
* Prints log messages depending on the debug level passed in.
Expand Down Expand Up @@ -66,21 +67,7 @@ class PeerOptions implements PeerJSOption {
serializers?: SerializerMapping;
}

class PeerError extends Error {
constructor(type: PeerErrorType, err: Error | string) {
if (typeof err === "string") {
super(err);
} else {
super();
Object.assign(this, err);
}

this.type = type;
}

type: PeerErrorType;
}
export type { PeerError, PeerOptions };
export { type PeerOptions };

export type SerializerMapping = {
[key: string]: new (
Expand All @@ -90,7 +77,7 @@ export type SerializerMapping = {
) => DataConnection;
};

export type PeerEvents = {
export interface PeerEvents {
/**
* Emitted when a connection to the PeerServer is established.
*
Expand Down Expand Up @@ -118,12 +105,12 @@ export type PeerEvents = {
*
* Errors from the underlying socket and PeerConnections are forwarded here.
*/
error: (error: PeerError) => void;
};
error: (error: PeerError<`${PeerErrorType}`>) => void;
}
/**
* A peer who can initiate connections with other peers.
*/
export class Peer extends EventEmitter<PeerEvents> {
export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
private static readonly DEFAULT_KEY = "peerjs";

protected readonly _serializers: SerializerMapping = {
Expand Down Expand Up @@ -500,7 +487,7 @@ export class Peer extends EventEmitter<PeerEvents> {
* @param peer The brokering ID of the remote peer (their {@apilink Peer.id}).
* @param options for specifying details about Peer Connection
*/
connect(peer: string, options: PeerConnectOption): DataConnection {
connect(peer: string, options: PeerConnectOption = {}): DataConnection {
options = {
serialization: "default",
...options,
Expand Down Expand Up @@ -640,13 +627,6 @@ export class Peer extends EventEmitter<PeerEvents> {
}
}

/** Emits a typed error message. */
emitError(type: PeerErrorType, err: string | Error): void {
logger.error("Error:", err);

this.emit("error", new PeerError(type, err));
}

/**
* Destroys the Peer: closes all active connections as well as the connection
* to the server.
Expand Down
44 changes: 44 additions & 0 deletions lib/peerError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { EventEmitter } from "eventemitter3";
import logger from "./logger";

export interface EventsWithError<ErrorType extends string> {
error: (error: PeerError<`${ErrorType}`>) => void;
}

export class EventEmitterWithError<
ErrorType extends string,
Events extends EventsWithError<ErrorType>,
> extends EventEmitter<Events, never> {
/**
* Emits a typed error message.
*
* @internal
*/
emitError(type: ErrorType, err: string | Error): void {
logger.error("Error:", err);

// @ts-ignore
this.emit("error", new PeerError<`${ErrorType}`>(`${type}`, err));
}
}
/**
* A PeerError is emitted whenever an error occurs.
* It always has a `.type`, which can be used to identify the error.
*/
export class PeerError<T extends string> extends Error {
/**
* @internal
*/
constructor(type: T, err: Error | string) {
if (typeof err === "string") {
super(err);
} else {
super();
Object.assign(this, err);
}

this.type = type;
}

public type: T;
}

0 comments on commit ad3a0cb

Please sign in to comment.