diff --git a/.DS_Store b/.DS_Store
index 656b2ad..78964ce 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/example/host.html b/example/host.html
index 49f60ff..82e3cf5 100644
--- a/example/host.html
+++ b/example/host.html
@@ -6,18 +6,27 @@
document.getElementById("hostId").value = host.hostId;
alert("Host ready!");
});
- host.on("error", console.error);
+ host.on("error", function (error) {
+ if (error instanceof uhst.RelayError) {
+ alert("Disconnected.");
+ } else {
+ console.error(error);
+ }
+ });
host.on("diagnostic", console.log);
host.on("connection", function connection(ws) {
ws.on("diagnostic", console.log);
ws.on("message", function incoming(message) {
console.log("Host received: %s from %s", message, ws.remoteId);
- host.broadcast("Host received: "+ message);
+ host.broadcast("Host received: " + message);
});
ws.on("open", function ready() {
console.log("Client %s connected", ws.remoteId);
ws.send("something");
});
+ ws.on("close", function disconnected() {
+ console.log("Client %s disconnected.", ws.remoteId);
+ });
});
-Host ID:
\ No newline at end of file
+Host ID:
\ No newline at end of file
diff --git a/example/join.html b/example/join.html
index 657e869..79211ed 100644
--- a/example/join.html
+++ b/example/join.html
@@ -23,8 +23,11 @@
ws.on("message", function incoming(message) {
console.log("Client received: %s", message);
});
+ ws.on("close", function close() {
+ console.log("Connection to host %s dropped.", ws.remoteId);
+ });
}
-Host ID:
+Host ID:
\ No newline at end of file
diff --git a/lib/ApiClient.ts b/lib/ApiClient.ts
index 2f33525..4440eea 100644
--- a/lib/ApiClient.ts
+++ b/lib/ApiClient.ts
@@ -1,6 +1,7 @@
import {
MessageHandler,
MessageStream,
+ RelayEventHandler,
UhstRelayClient,
} from './contracts/UhstRelayClient';
import { HostConfiguration, ClientConfiguration } from './models';
@@ -71,9 +72,17 @@ export class ApiClient implements UhstRelayClient {
subscribeToMessages(
token: string,
- handler: MessageHandler,
+ messageHandler: MessageHandler,
+ relayErrorHandler: Function,
+ relayEventHandler: RelayEventHandler,
receiveUrl?: string
): Promise {
- return this.relayClient.subscribeToMessages(token, handler, receiveUrl);
+ return this.relayClient.subscribeToMessages(
+ token,
+ messageHandler,
+ relayErrorHandler,
+ relayEventHandler,
+ receiveUrl
+ );
}
}
diff --git a/lib/NetworkClient.ts b/lib/NetworkClient.ts
index 0953624..989145c 100644
--- a/lib/NetworkClient.ts
+++ b/lib/NetworkClient.ts
@@ -4,28 +4,46 @@ const REQUEST_OPTIONS = {
method: 'POST',
};
+const getRequestOptions = (body: any): any => {
+ if (body) {
+ return {
+ ...REQUEST_OPTIONS,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(body),
+ };
+ } else {
+ return REQUEST_OPTIONS;
+ }
+};
+
export class NetworkClient {
async post(
url: string,
queryParams?: string[],
+ body?: any,
timeout?: number
): Promise {
+ if (queryParams && queryParams.length > 0) {
+ url = `${url}?${queryParams.join('&')}`;
+ }
+ let response: Response;
try {
- if (queryParams && queryParams.length > 0) {
- url = `${url}?${queryParams.join('&')}`;
- }
- const response = timeout
- ? await this.fetchWithTimeout(url, { ...REQUEST_OPTIONS, timeout })
- : await fetch(url, REQUEST_OPTIONS);
- if (response.status == 200) {
- return response.json();
- } else {
- throw new NetworkError(response.status, `${response.statusText}`);
- }
+ response = timeout
+ ? await this.fetchWithTimeout(url, {
+ ...getRequestOptions(body),
+ timeout,
+ })
+ : await fetch(url, getRequestOptions(body));
} catch (error) {
- console.log(error);
throw new NetworkUnreachable(error);
}
+ if (response.status == 200) {
+ return response.json();
+ } else {
+ throw new NetworkError(response.status, `${response.statusText}`);
+ }
}
async get(
@@ -33,22 +51,22 @@ export class NetworkClient {
queryParams?: string[],
timeout?: number
): Promise {
+ if (queryParams && queryParams.length > 0) {
+ url = `${url}?${queryParams.join('&')}`;
+ }
+ let response: Response;
try {
- if (queryParams && queryParams.length > 0) {
- url = `${url}?${queryParams.join('&')}`;
- }
- const response = timeout
+ response = timeout
? await this.fetchWithTimeout(url, { timeout })
: await fetch(url);
- if (response.status == 200) {
- return response.json();
- } else {
- throw new NetworkError(response.status, `${response.statusText}`);
- }
} catch (error) {
- console.log(error);
throw new NetworkUnreachable(error);
}
+ if (response.status == 200) {
+ return response.json();
+ } else {
+ throw new NetworkError(response.status, `${response.statusText}`);
+ }
}
async fetchWithTimeout(resource, options): Promise {
diff --git a/lib/RelayClient.ts b/lib/RelayClient.ts
index 19f297b..af37697 100644
--- a/lib/RelayClient.ts
+++ b/lib/RelayClient.ts
@@ -2,8 +2,9 @@ import {
UhstRelayClient,
MessageHandler,
MessageStream,
+ RelayEventHandler,
} from './contracts/UhstRelayClient';
-import { ClientConfiguration, HostConfiguration, Message } from './models';
+import { ClientConfiguration, HostConfiguration, Message, RelayEvent } from './models';
import {
InvalidToken,
InvalidHostId,
@@ -15,13 +16,6 @@ import {
} from './UhstErrors';
import { NetworkClient } from './NetworkClient';
-const REQUEST_OPTIONS = {
- method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- },
-};
-
export class RelayClient implements UhstRelayClient {
networkClient: NetworkClient;
constructor(private relayUrl: string, networkClient?: NetworkClient) {
@@ -30,10 +24,11 @@ export class RelayClient implements UhstRelayClient {
async initHost(hostId?: string): Promise {
try {
- return this.networkClient.post(
+ const hostConfig = await this.networkClient.post(
this.relayUrl,
hostId ? ['action=host', `hostId=${hostId}`] : ['action=host']
);
+ return hostConfig;
} catch (error) {
if (error instanceof NetworkError) {
if (error.responseCode == 400) {
@@ -42,7 +37,6 @@ export class RelayClient implements UhstRelayClient {
throw new RelayError(error.message);
}
} else {
- console.log(error);
throw new RelayUnreachable(error);
}
}
@@ -50,10 +44,11 @@ export class RelayClient implements UhstRelayClient {
async initClient(hostId: string): Promise {
try {
- return this.networkClient.post(this.relayUrl, [
+ const clientConfig = await this.networkClient.post(this.relayUrl, [
'action=join',
`hostId=${hostId}`,
]);
+ return clientConfig;
} catch (error) {
if (error instanceof NetworkError) {
if (error.responseCode == 400) {
@@ -62,7 +57,6 @@ export class RelayClient implements UhstRelayClient {
throw new RelayError(error.message);
}
} else {
- console.log(error);
throw new RelayUnreachable(error);
}
}
@@ -72,47 +66,66 @@ export class RelayClient implements UhstRelayClient {
token: string,
message: any,
sendUrl?: string
- ): Promise {
+ ): Promise {
const url = sendUrl ?? this.relayUrl;
- let response: Response;
try {
- response = await fetch(`${url}?token=${token}`, {
- ...REQUEST_OPTIONS,
- body: JSON.stringify(message),
- });
+ const response = await this.networkClient.post(
+ url,
+ [`token=${token}`],
+ message
+ );
+ return response;
} catch (error) {
- console.log(error);
- throw new RelayUnreachable(error);
- }
- if (response.status == 200) {
- return;
- } else if (response.status == 400) {
- throw new InvalidClientOrHostId(response.statusText);
- } else if (response.status == 401) {
- throw new InvalidToken(response.statusText);
- } else {
- throw new RelayError(`${response.status} ${response.statusText}`);
+ if (error instanceof NetworkError) {
+ if (error.responseCode == 400) {
+ throw new InvalidClientOrHostId(error.message);
+ } else if (error.responseCode == 401) {
+ throw new InvalidToken(error.message);
+ } else {
+ throw new RelayError(`${error.responseCode} ${error.message}`);
+ }
+ } else {
+ throw new RelayUnreachable(error);
+ }
}
}
subscribeToMessages(
token: string,
- handler: MessageHandler,
+ messageHandler: MessageHandler,
+ relayErrorHandler: Function,
+ relayEventHandler?: RelayEventHandler,
receiveUrl?: string
): Promise {
const url = receiveUrl ?? this.relayUrl;
return new Promise((resolve, reject) => {
+ let resolved = false;
const stream = new EventSource(`${url}?token=${token}`);
- stream.onopen = (ev: Event) => {
- resolve(stream);
+ stream.onopen = () => {
+ if (!resolved) {
+ resolve(stream);
+ resolved = true;
+ }
};
- stream.onerror = (ev: Event) => {
- reject(new RelayError(ev));
+ stream.onerror = () => {
+ if (!resolved) {
+ // error on connect
+ reject(new RelayError());
+ resolved = true;
+ } else if (relayErrorHandler) {
+ relayErrorHandler(new RelayError());
+ }
};
stream.addEventListener('message', (evt: MessageEvent) => {
const message: Message = JSON.parse(evt.data);
- handler(message);
+ messageHandler(message);
});
+ if (relayEventHandler) {
+ stream.addEventListener('relay_event', (evt: MessageEvent) => {
+ const relayEvent: RelayEvent = JSON.parse(evt.data);
+ relayEventHandler(relayEvent);
+ });
+ }
});
}
}
diff --git a/lib/RelaySocket.ts b/lib/RelaySocket.ts
index 3abb776..493abc4 100644
--- a/lib/RelaySocket.ts
+++ b/lib/RelaySocket.ts
@@ -1,95 +1,154 @@
-import { EventEmitter } from "inf-ee";
-import { MessageStream, UhstRelayClient } from "./contracts/UhstRelayClient";
-import { SocketEventSet, UhstSocket } from "./contracts/UhstSocket";
-import { ClientSocketParams, HostSocketParams, Message } from "./models";
+import { EventEmitter } from 'inf-ee';
+import { MessageStream, UhstRelayClient } from './contracts/UhstRelayClient';
+import { SocketEventSet, UhstSocket } from './contracts/UhstSocket';
+import { ClientSocketParams, HostSocketParams, Message, RelayEvent, RelayEventType } from './models';
export class RelaySocket implements UhstSocket {
- private _ee = new EventEmitter();
- private token: string;
- private _remoteId: string;
- private relayMessageStream: MessageStream;
- private sendUrl?: string;
+ private _ee = new EventEmitter();
+ private token: string;
+ private _remoteId: string;
+ private relayMessageStream?: MessageStream;
+ private sendUrl?: string;
- constructor(private relayClient: UhstRelayClient, params: HostSocketParams | ClientSocketParams, private debug: boolean) {
- this.send = this.send.bind(this);
- this.handleMessage = this.handleMessage.bind(this);
- this.close = this.close.bind(this);
+ constructor(
+ private relayClient: UhstRelayClient,
+ params: HostSocketParams | ClientSocketParams,
+ private debug: boolean
+ ) {
+ this.send = this.send.bind(this);
+ this.handleMessage = this.handleMessage.bind(this);
+ this.close = this.close.bind(this);
- switch (params.type) {
- case "client":
- // will connect to host
- this.initClient(params.hostId);
- this._remoteId = params.hostId;
- break;
- case "host":
- // client connected
- this.token = params.token;
- this.sendUrl = params.sendUrl;
- this._remoteId = params.clientId;
- // give consumer a chance to subscribe to open event
- setTimeout(() => {
- this._ee.emit("open");
- });
- break;
- default:
- throw Error("Unsupported Socket Parameters Type");
- }
- }
- get remoteId(): string {
- return this._remoteId;
+ switch (params.type) {
+ case 'client':
+ // will connect to host
+ this.initClient(params.hostId);
+ this._remoteId = params.hostId;
+ break;
+ case 'host':
+ // client connected
+ this.token = params.token;
+ this.sendUrl = params.sendUrl;
+ this._remoteId = params.clientId;
+ // give consumer a chance to subscribe to open event
+ setTimeout(() => {
+ this._ee.emit('open');
+ });
+ break;
+ default:
+ throw Error('Unsupported Socket Parameters Type');
}
+ }
+ get remoteId(): string {
+ return this._remoteId;
+ }
- on(eventName: EventName, handler: SocketEventSet[EventName]) {
- this._ee.on(eventName, handler);
- }
+ on(
+ eventName: EventName,
+ handler: SocketEventSet[EventName]
+ ) {
+ this._ee.on(eventName, handler);
+ }
- once(eventName: EventName, handler: SocketEventSet[EventName]) {
- this._ee.once(eventName, handler);
- }
+ once(
+ eventName: EventName,
+ handler: SocketEventSet[EventName]
+ ) {
+ this._ee.once(eventName, handler);
+ }
- off(eventName: EventName, handler: SocketEventSet[EventName]) {
- this._ee.off(eventName, handler);
+ off(
+ eventName: EventName,
+ handler: SocketEventSet[EventName]
+ ) {
+ this._ee.off(eventName, handler);
+ }
+
+ send(message: string): Promise;
+ send(message: Blob): Promise;
+ send(message: ArrayBuffer): Promise;
+ send(message: ArrayBufferView): Promise;
+ async send(message: any): Promise {
+ const envelope = {
+ type: 'string',
+ payload: message,
+ };
+ try {
+ await this.relayClient.sendMessage(this.token, envelope, this.sendUrl);
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Sent message ' + message);
+ }
+ } catch (error) {
+ if (this.debug) {
+ this._ee.emit(
+ 'diagnostic',
+ 'Failed sending message: ' + JSON.stringify(error)
+ );
+ }
+ this._ee.emit('error', error);
}
-
- send(message: string): Promise;
- send(message: Blob): Promise;
- send(message: ArrayBuffer): Promise;
- send(message: ArrayBufferView): Promise;
- async send(message: any): Promise {
- const envelope = {
- "type": "string",
- "payload": message
- }
- await this.relayClient.sendMessage(this.token, envelope, this.sendUrl).catch((error) => {
- if (this.debug) { this._ee.emit("diagnostic", "Failed sending message: " + JSON.stringify(error)); }
- this._ee.emit("error", error);
- });
- if (this.debug) { this._ee.emit("diagnostic", "Sent message " + message); }
+ }
+
+ close() {
+ this.relayMessageStream?.close();
+ this._ee.emit('close');
+ }
+
+ handleMessage = (message: Message) => {
+ const payload = message.body.payload;
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Message received: ' + payload);
}
+ this._ee.emit('message', payload);
+ }
- close() {
- this.relayMessageStream?.close();
+ handleRelayEvent = (event: RelayEvent) => {
+ if (event.eventType === RelayEventType.HOST_CLOSED) {
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Host disconnected from relay.');
+ }
+ this.close();
}
+ }
- handleMessage(message: Message) {
- const payload = message.body.payload
- if (this.debug) { this._ee.emit("diagnostic", "Message received: " + payload); }
- this._ee.emit("message", payload);
+ handleRelayError = () => {
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Client connection to relay dropped.');
}
+ this.close();
+ }
- private async initClient(hostId: string) {
- try {
- const config = await this.relayClient.initClient(hostId);
- if (this.debug) { this._ee.emit("diagnostic", "Client configuration received from server."); }
- this.token = config.clientToken;
- this.sendUrl = config.sendUrl;
- this.relayMessageStream = await this.relayClient.subscribeToMessages(config.clientToken, this.handleMessage, config.receiveUrl);
- if (this.debug) { this._ee.emit("diagnostic", "Client subscribed to messages from server."); }
- this._ee.emit("open");
- } catch (error) {
- if (this.debug) { this._ee.emit("diagnostic", "Client failed: " + JSON.stringify(error)); }
- this._ee.emit("error", error);
- }
+ private async initClient(hostId: string) {
+ try {
+ const config = await this.relayClient.initClient(hostId);
+ if (this.debug) {
+ this._ee.emit(
+ 'diagnostic',
+ 'Client configuration received from server.'
+ );
+ }
+ this.token = config.clientToken;
+ this.sendUrl = config.sendUrl;
+ this.relayMessageStream = await this.relayClient.subscribeToMessages(
+ config.clientToken,
+ this.handleMessage,
+ this.handleRelayError,
+ this.handleRelayEvent,
+ config.receiveUrl
+ );
+ if (this.debug) {
+ this._ee.emit(
+ 'diagnostic',
+ 'Client subscribed to messages from server.'
+ );
+ }
+ this._ee.emit('open');
+ } catch (error) {
+ this.relayMessageStream = undefined;
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Client failed: ' + JSON.stringify(error));
+ }
+ this._ee.emit('error', error);
}
-
-}
\ No newline at end of file
+ }
+}
diff --git a/lib/UhstHost.ts b/lib/UhstHost.ts
index a9e6362..5564ea0 100644
--- a/lib/UhstHost.ts
+++ b/lib/UhstHost.ts
@@ -1,88 +1,167 @@
-import JwtDecode from "jwt-decode";
-import { EventEmitter } from "inf-ee";
-import { MessageStream, UhstRelayClient } from "./contracts/UhstRelayClient";
-import { HostConfiguration, HostMessage } from "./models";
-import { UhstSocket } from "./contracts/UhstSocket";
-import { UhstSocketProvider } from "./contracts/UhstSocketProvider";
+import JwtDecode from 'jwt-decode';
+import { EventEmitter } from 'inf-ee';
+import { MessageStream, UhstRelayClient } from './contracts/UhstRelayClient';
+import {
+ HostConfiguration,
+ HostMessage,
+ RelayEvent,
+ RelayEventType,
+} from './models';
+import { UhstSocket } from './contracts/UhstSocket';
+import { UhstSocketProvider } from './contracts/UhstSocketProvider';
+import { RelayError } from './UhstErrors';
type HostEventSet = {
- ready: () => void,
- connection: (socket: UhstSocket) => void,
- error: (error: Error) => void,
- diagnostic: (message: string) => void
-}
+ ready: () => void;
+ connection: (socket: UhstSocket) => void;
+ error: (error: Error) => void;
+ diagnostic: (message: string) => void;
+};
export class UhstHost {
- private _ee = new EventEmitter();
- private clients = new Map();
- private config: HostConfiguration;
- private relayMessageStream: MessageStream;
+ private _ee = new EventEmitter();
+ private clients = new Map();
+ private config: HostConfiguration;
+ private relayMessageStream?: MessageStream;
- constructor(private relayClient: UhstRelayClient, private socketProvider: UhstSocketProvider, requestedHostId: string | undefined, private debug: boolean) {
- this.handleMessage = this.handleMessage.bind(this);
-
- this.init(requestedHostId);
- }
+ constructor(
+ private relayClient: UhstRelayClient,
+ private socketProvider: UhstSocketProvider,
+ requestedHostId: string | undefined,
+ private debug: boolean
+ ) {
+ this.handleMessage = this.handleMessage.bind(this);
- get hostId(): string {
- return this.config.hostId;
- }
+ this.init(requestedHostId);
+ }
- broadcast(message: string): void;
- broadcast(message: Blob): void;
- broadcast(message: ArrayBuffer): void;
- broadcast(message: ArrayBufferView): void;
- broadcast(message: any) {
- const envelope = {
- "type": "string",
- "payload": message
- }
- this.relayClient.sendMessage(this.config.hostToken, envelope, this.config.sendUrl).catch((error) => {
- if (this.debug) { this._ee.emit("diagnostic", "Failed sending message: " + JSON.stringify(error)); }
- this._ee.emit("error", error);
- });
- if (this.debug) { this._ee.emit("diagnostic", "Sent message " + message); }
- }
+ get hostId(): string {
+ return this.config.hostId;
+ }
- on(eventName: EventName, handler: HostEventSet[EventName]) {
- this._ee.on(eventName, handler);
+ broadcast(message: string): Promise;
+ broadcast(message: Blob): Promise;
+ broadcast(message: ArrayBuffer): Promise;
+ broadcast(message: ArrayBufferView): Promise;
+ async broadcast(message: any): Promise {
+ const envelope = {
+ type: 'string',
+ payload: message,
+ };
+ try {
+ await this.relayClient.sendMessage(
+ this.config.hostToken,
+ envelope,
+ this.config.sendUrl
+ );
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Sent message ' + message);
+ }
+ } catch (error) {
+ if (this.debug) {
+ this._ee.emit(
+ 'diagnostic',
+ 'Failed sending message: ' + JSON.stringify(error)
+ );
+ }
+ this._ee.emit('error', error);
}
+ }
- once(eventName: EventName, handler: HostEventSet[EventName]) {
- this._ee.once(eventName, handler);
- }
+ on(
+ eventName: EventName,
+ handler: HostEventSet[EventName]
+ ) {
+ this._ee.on(eventName, handler);
+ }
- off(eventName: EventName, handler: HostEventSet[EventName]) {
- this._ee.off(eventName, handler);
+ once(
+ eventName: EventName,
+ handler: HostEventSet[EventName]
+ ) {
+ this._ee.once(eventName, handler);
+ }
+
+ off(
+ eventName: EventName,
+ handler: HostEventSet[EventName]
+ ) {
+ this._ee.off(eventName, handler);
+ }
+
+ disconnect() {
+ this.relayMessageStream?.close();
+ }
+
+ private handleMessage = (message: HostMessage) => {
+ const clientId: string = (JwtDecode(message.responseToken) as any).clientId;
+ let hostSocket = this.clients.get(clientId);
+ if (!hostSocket) {
+ const socket = this.socketProvider.createUhstSocket(
+ this.relayClient,
+ {
+ type: 'host',
+ token: message.responseToken,
+ sendUrl: this.config.sendUrl,
+ clientId,
+ },
+ this.debug
+ );
+ if (this.debug) {
+ this._ee.emit(
+ 'diagnostic',
+ 'Host received client connection from clientId: ' + clientId
+ );
+ }
+ this._ee.emit('connection', socket);
+ this.clients.set(clientId, socket);
+ hostSocket = socket;
}
+ hostSocket.handleMessage(message);
+ }
- disconnect() {
- this.relayMessageStream?.close();
+ private handleRelayEvent = (event: RelayEvent) => {
+ if (event.eventType === RelayEventType.CLIENT_CLOSED) {
+ const clientId = event.body;
+ this.clients.get(clientId)?.close();
+ this.clients.delete(clientId);
}
+ }
- private handleMessage(message: HostMessage) {
- const clientId: string = (JwtDecode(message.responseToken) as any).clientId;
- let hostSocket = this.clients.get(clientId);
- if (!hostSocket) {
- const socket = this.socketProvider.createUhstSocket(this.relayClient, {type: "host", token: message.responseToken, sendUrl: this.config.sendUrl, clientId}, this.debug);
- if (this.debug) { this._ee.emit("diagnostic", "Host received client connection from clientId: "+clientId); }
- this._ee.emit("connection", socket);
- this.clients.set(clientId, socket);
- hostSocket = socket;
- }
- hostSocket.handleMessage(message);
+ private handleRelayError = () => {
+ this.relayMessageStream?.close();
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Host connection to relay dropped.');
}
+ this._ee.emit('error', new RelayError());
+ }
- private async init(requestedHostId?: string) {
- try {
- this.config = await this.relayClient.initHost(requestedHostId);
- if (this.debug) { this._ee.emit("diagnostic", "Host configuration received from server."); }
- this.relayMessageStream = await this.relayClient.subscribeToMessages(this.config.hostToken, this.handleMessage, this.config.receiveUrl);
- if (this.debug) { this._ee.emit("diagnostic", "Host subscribed to messages from server."); }
- this._ee.emit("ready");
- } catch (error) {
- if (this.debug) { this._ee.emit("diagnostic", "Host failed subscribing to messages: "+JSON.stringify(error)); }
- this._ee.emit("error", error);
- }
+ private async init(requestedHostId?: string) {
+ try {
+ this.config = await this.relayClient.initHost(requestedHostId);
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Host configuration received from server.');
+ }
+ this.relayMessageStream = await this.relayClient.subscribeToMessages(
+ this.config.hostToken,
+ this.handleMessage,
+ this.handleRelayError,
+ this.handleRelayEvent,
+ this.config.receiveUrl
+ );
+ if (this.debug) {
+ this._ee.emit('diagnostic', 'Host subscribed to messages from server.');
+ }
+ this._ee.emit('ready');
+ } catch (error) {
+ this.relayMessageStream = undefined;
+ if (this.debug) {
+ this._ee.emit(
+ 'diagnostic',
+ 'Host message subscription failed: ' + JSON.stringify(error)
+ );
+ }
+ this._ee.emit('error', error);
}
-}
\ No newline at end of file
+ }
+}
diff --git a/lib/WebRTCSocket.ts b/lib/WebRTCSocket.ts
index 39fe993..b602faa 100644
--- a/lib/WebRTCSocket.ts
+++ b/lib/WebRTCSocket.ts
@@ -178,7 +178,7 @@ export class WebRTCSocket implements UhstSocket {
if (this.debug) { this._ee.emit("diagnostic", "Client configuration received from server."); }
this.token = config.clientToken;
this.sendUrl = config.sendUrl;
- this.relayMessageStream = await this.relayClient.subscribeToMessages(config.clientToken, this.handleMessage, config.receiveUrl);
+ this.relayMessageStream = await this.relayClient.subscribeToMessages(config.clientToken, this.handleMessage, undefined, undefined, config.receiveUrl);
if (this.debug) { this._ee.emit("diagnostic", "Client subscribed to messages from server."); }
const offer = await this.connection.createOffer();
this.relayClient.sendMessage(this.token, offer, this.sendUrl).then(() => {
diff --git a/lib/contracts/UhstRelayClient.ts b/lib/contracts/UhstRelayClient.ts
index 31707c9..a5990ca 100644
--- a/lib/contracts/UhstRelayClient.ts
+++ b/lib/contracts/UhstRelayClient.ts
@@ -1,14 +1,30 @@
-import { ClientConfiguration, HostConfiguration, Message } from "../models";
+import {
+ ClientConfiguration,
+ HostConfiguration,
+ Message,
+ RelayEvent,
+} from '../models';
export interface MessageHandler {
- (message: Message): void;
+ (message: Message): void;
}
+
+export interface RelayEventHandler {
+ (event: RelayEvent): void;
+}
+
export interface MessageStream {
- close():void;
+ close(): void;
}
export interface UhstRelayClient {
- initHost(hostId?: string): Promise;
- initClient(hostId: string): Promise;
- sendMessage(token: string, message: any, sendUrl?:string): Promise;
- subscribeToMessages(token: string, handler: MessageHandler, receiveUrl?: string): Promise;
-}
\ No newline at end of file
+ initHost(hostId?: string): Promise;
+ initClient(hostId: string): Promise;
+ sendMessage(token: string, message: any, sendUrl?: string): Promise;
+ subscribeToMessages(
+ token: string,
+ messageHandler: MessageHandler,
+ relayErrorHandler?: Function,
+ relayEventHandler?: RelayEventHandler,
+ receiveUrl?: string
+ ): Promise;
+}
diff --git a/lib/models/RelayEvent.ts b/lib/models/RelayEvent.ts
new file mode 100644
index 0000000..f7e8c4b
--- /dev/null
+++ b/lib/models/RelayEvent.ts
@@ -0,0 +1,14 @@
+import { Message } from './Message';
+
+export enum RelayEventType {
+ CLIENT_CLOSED = 'client_closed',
+ HOST_CLOSED = 'host_closed',
+}
+
+export interface RelayEvent extends Message {
+ eventType: RelayEventType;
+}
+
+export function isRelayEvent(obj: any): obj is RelayEvent {
+ return obj.eventType !== undefined;
+}
diff --git a/lib/models/index.ts b/lib/models/index.ts
index 74d141b..06cfe5c 100644
--- a/lib/models/index.ts
+++ b/lib/models/index.ts
@@ -4,4 +4,5 @@ export * from './HostConfiguration';
export * from './HostMessage';
export * from './Message';
export * from './SocketParams';
-export * from './RelayMessage';
\ No newline at end of file
+export * from './RelayMessage';
+export * from './RelayEvent';
\ No newline at end of file
diff --git a/test/RelaySocket.spec.ts b/test/RelaySocket.spec.ts
index 688190a..259822b 100644
--- a/test/RelaySocket.spec.ts
+++ b/test/RelaySocket.spec.ts
@@ -23,7 +23,7 @@ describe("# RelaySocket", () => {
receiveUrl: "testReceiveUrl",
sendUrl: "testSendUrl",
});
- mockRelay.subscribeToMessages = (clientToken, handleMessage, receiveUrl) => {
+ mockRelay.subscribeToMessages = (clientToken, handleMessage, handleRelayError, handleRelayEvent, receiveUrl) => {
expect(clientToken).to.equal("testClientToken");
expect(receiveUrl).to.equal("testReceiveUrl");
messageHandler = handleMessage;
diff --git a/test/UhstHost.spec.ts b/test/UhstHost.spec.ts
index 0556992..2b5b45a 100644
--- a/test/UhstHost.spec.ts
+++ b/test/UhstHost.spec.ts
@@ -25,7 +25,7 @@ describe("# UhstHost", () => {
sendUrl: "testSendUrl",
});
- mockRelay.subscribeToMessages = (token, handler, receiveUrl) => {
+ mockRelay.subscribeToMessages = (token, handler, handleRelayError, handleRelayEvent, receiveUrl) => {
expect(token).to.equal("testHostToken");
expect(receiveUrl).to.equal("testReceiveUrl");
messageHandler = handler;
@@ -78,7 +78,7 @@ describe("# UhstHost", () => {
sendUrl: "testSendUrl",
});
- mockRelay.subscribeToMessages = (token, handler, receiveUrl) => {
+ mockRelay.subscribeToMessages = (token, handler, handleRelayError, handleRelayEvent, receiveUrl) => {
expect(token).to.equal("testHostToken");
expect(receiveUrl).to.equal("testReceiveUrl");
messageHandler = handler;
diff --git a/test/WebRTCSocket.spec.ts b/test/WebRTCSocket.spec.ts
index c9cde62..e94fc5d 100644
--- a/test/WebRTCSocket.spec.ts
+++ b/test/WebRTCSocket.spec.ts
@@ -141,7 +141,7 @@
// receiveUrl: "testReceiveUrl",
// sendUrl: "testSendUrl",
// });
-// mockRelay.subscribeToMessages = (clientToken, handleMessage, receiveUrl) => {
+// mockRelay.subscribeToMessages = (clientToken, handleMessage, handleRelayError, handleRelayEvent, receiveUrl) => {
// expect(clientToken).to.equal("testClientToken");
// expect(receiveUrl).to.equal("testReceiveUrl");
// messageHandler = handleMessage;