From 752f3290360156021ec97a732204a52bfd7d9ff6 Mon Sep 17 00:00:00 2001 From: moozzyk Date: Mon, 31 Oct 2016 16:46:10 -0700 Subject: [PATCH] WIP: - separating Connection from RpcConnection - fixing WebSockets transport to work after rewrite - clean up --- .../Common.ts | 2 + .../Connection.ts | 92 +++++++++++++++++++ .../ITransport.ts | 4 +- .../LongPollingTransport.ts | 5 +- .../RpcConnection.ts | 54 +++++------ .../ServerSentEventsTransport.ts | 5 +- .../WebSocketTransport.ts | 17 ++-- 7 files changed, 136 insertions(+), 43 deletions(-) create mode 100644 src/Microsoft.AspNetCore.SignalR.Client.TS/Common.ts create mode 100644 src/Microsoft.AspNetCore.SignalR.Client.TS/Connection.ts diff --git a/src/Microsoft.AspNetCore.SignalR.Client.TS/Common.ts b/src/Microsoft.AspNetCore.SignalR.Client.TS/Common.ts new file mode 100644 index 0000000000..1d6179af17 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client.TS/Common.ts @@ -0,0 +1,2 @@ +declare type DataReceived = (data: any) => void; +declare type ErrorHandler = (e: any) => void; \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SignalR.Client.TS/Connection.ts b/src/Microsoft.AspNetCore.SignalR.Client.TS/Connection.ts new file mode 100644 index 0000000000..649c22ddf9 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client.TS/Connection.ts @@ -0,0 +1,92 @@ + +enum ConnectionState { + Disconnected, + Connecting, + Connected +} + +class Connection { + private connectionState: ConnectionState; + private url: string; + private queryString: string; + private connectionId: string; + private transport: ITransport; + private dataReceivedCallback: DataReceived; + private errorHandler: ErrorHandler; + + constructor(url: string, queryString: string = "") { + this.url = url; + this.queryString = queryString; + + this.connectionState = ConnectionState.Disconnected; + } + + start(): Promise { + if (this.connectionState != ConnectionState.Disconnected) { + throw new Error("Cannot start a connection that is not in the 'Disconnected' state"); + } + + return new HttpClient().get(`${this.url}/getid?${this.queryString}`) + .then(connectionId => { + this.connectionId = connectionId; + return this.tryStartTransport([ + new WebSocketTransport(), + new ServerSentEventsTransport(null), + new LongPollingTransport(null)], 0); + }) + .then(transport => { + this.transport = transport; + this.connectionState = ConnectionState.Connected; + }) + .catch(e => { + console.log("Failed to start the connection.") + this.connectionState = ConnectionState.Disconnected; + throw e; + }); + } + + private tryStartTransport(transports: ITransport[], index: number): Promise { + let thisConnection = this; + transports[index].onDataReceived = data => thisConnection.dataReceivedCallback(data); + transports[index].onError = e => thisConnection.errorHandler(e); + + return transports[index].connect(this.url, this.queryString) + .then(() => { + return transports[index]; + }) + .catch(e => { + index++; + if (index < transports.length) { + return this.tryStartTransport(transports, index); + } + else + { + throw new Error('No transport could be started.') + } + }) + } + + send(data: any): Promise { + if (this.connectionState != ConnectionState.Connected) { + throw new Error("Cannot send data if the connection is not in the 'Connected' State"); + } + return this.transport.send(data); + } + + set dataReceived(callback: DataReceived) { + this.dataReceivedCallback = callback; + } + + set onError(callback: ErrorHandler) { + this.errorHandler = callback; + } + + stop(): void { + if (this.connectionState != ConnectionState.Connected) { + throw new Error("Cannot stop the connection if is not in the 'Connected' State"); + } + + this.transport.stop(); + this.connectionState = ConnectionState.Disconnected; + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SignalR.Client.TS/ITransport.ts b/src/Microsoft.AspNetCore.SignalR.Client.TS/ITransport.ts index 92fd8de54f..2b561c8359 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.TS/ITransport.ts +++ b/src/Microsoft.AspNetCore.SignalR.Client.TS/ITransport.ts @@ -1,5 +1,7 @@ interface ITransport { connect(url: string, queryString: string): Promise; - send(data: string): Promise; + send(data: any): Promise; stop(): void; + onDataReceived: DataReceived; + onError: ErrorHandler; } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SignalR.Client.TS/LongPollingTransport.ts b/src/Microsoft.AspNetCore.SignalR.Client.TS/LongPollingTransport.ts index 6630541d80..a3bfec51a3 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.TS/LongPollingTransport.ts +++ b/src/Microsoft.AspNetCore.SignalR.Client.TS/LongPollingTransport.ts @@ -51,11 +51,14 @@ class LongPollingTransport implements ITransport { }; } - send(data: string): Promise { + send(data: any): Promise { return new HttpClient().post(this.url + "/send?" + this.queryString, data); } stop(): void { this.pollXhr.abort(); } + + onDataReceived: DataReceived; + onError: ErrorHandler; } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SignalR.Client.TS/RpcConnection.ts b/src/Microsoft.AspNetCore.SignalR.Client.TS/RpcConnection.ts index fd92d3eb18..d3d700ae44 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.TS/RpcConnection.ts +++ b/src/Microsoft.AspNetCore.SignalR.Client.TS/RpcConnection.ts @@ -11,23 +11,25 @@ interface InvocationResultDescriptor { } class RpcConnection { - // TODO: add connection state - private url: string; - private queryString: string; + private connection: Connection; private callbacks: Map void>; private methods: Map void>; - private transport: ITransport; private id: number; constructor(url: string, queryString?: string) { - this.url = url; - this.queryString = queryString || ""; + this.connection = new Connection(url, queryString); + + let thisRpcConnection = this; + this.connection.dataReceived = data => { + thisRpcConnection.dataReceived(data); + }; + this.callbacks = new Map void>(); this.methods = new Map void>(); this.id = 0; } - private messageReceived(data: string) { + private dataReceived(data: any) { //TODO: separate JSON parsing var descriptor = JSON.parse(data); if (descriptor.Method === undefined) { @@ -49,37 +51,26 @@ class RpcConnection { } start(): Promise { - return new Promise((resolve, reject) => { - new HttpClient().get(this.url + "/getid?" + this.queryString) - .then(id => { - this.transport = new ServerSentEventsTransport(data => this.messageReceived(data)); - // this.transport = new WebSocketTransport(data => this.messageReceived(data)); - // this.transport = new WebSocketTransport(data => this.messageReceived(data)); - // this.transport = new LongPollingTransport(data => this.messageReceived(data)); - return this.transport.connect(this.url, `id=${id}&${this.queryString}`); - }) - .then(() => { - resolve(); - }) - .catch(e => { - reject(e); - }); - }); + return this.connection.start(); } stop(): void { - this.transport.stop(); + return this.connection.stop(); } invoke(methodName: string, ...args: any[]): Promise { + + let id = this.id; + this.id++; + let invocationDescriptor: InvocationDescriptor = { - "Id": this.id.toString(), + "Id": id.toString(), "Method": methodName, "Arguments": args }; let p = new Promise((resolve, reject) => { - this.callbacks[this.id] = (invocationResult: InvocationResultDescriptor) => { + this.callbacks[id] = (invocationResult: InvocationResultDescriptor) => { if (invocationResult.Error != null) { reject(invocationResult.Error); } @@ -88,13 +79,14 @@ class RpcConnection { } }; - this.transport.send(JSON.stringify(invocationDescriptor)) - .catch(e => reject(e)); + //TODO: separate conversion to enable different data formats + this.connection.send(JSON.stringify(invocationDescriptor)) + .catch(e => { + // TODO: remove callback + reject(e); + }); }); - this.id++; - - //TODO: separate conversion to enable different data formats return p; } diff --git a/src/Microsoft.AspNetCore.SignalR.Client.TS/ServerSentEventsTransport.ts b/src/Microsoft.AspNetCore.SignalR.Client.TS/ServerSentEventsTransport.ts index 1c5be418ca..9529359a31 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.TS/ServerSentEventsTransport.ts +++ b/src/Microsoft.AspNetCore.SignalR.Client.TS/ServerSentEventsTransport.ts @@ -23,11 +23,14 @@ class ServerSentEventsTransport implements ITransport { return Promise.resolve(); } - send(data: string): Promise { + send(data: any): Promise { return new HttpClient().post(this.url + "/send?" + this.queryString, data); } stop(): void { this.eventSource.close(); } + + onDataReceived: DataReceived; + onError: ErrorHandler; } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SignalR.Client.TS/WebSocketTransport.ts b/src/Microsoft.AspNetCore.SignalR.Client.TS/WebSocketTransport.ts index 817ea0f070..b7bc31f7f4 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.TS/WebSocketTransport.ts +++ b/src/Microsoft.AspNetCore.SignalR.Client.TS/WebSocketTransport.ts @@ -1,12 +1,5 @@ class WebSocketTransport implements ITransport { private webSocket: WebSocket; - // TODO: make the callback a named type - // TODO: string won't work for binary formats - private receiveCallback: (data: string) => void; - - constructor(receiveCallback: (data: string) => void) { - this.receiveCallback = receiveCallback; - } connect(url: string, queryString: string): Promise { return new Promise((resolve, reject) => { @@ -24,12 +17,15 @@ class WebSocketTransport implements ITransport { }; this.webSocket.onmessage = (message: MessageEvent) => { - this.receiveCallback(message.data); + console.log(`(WebSockets transport) data received: ${message.data}`); + if (this.onDataReceived) { + this.onDataReceived(message.data); + } } }); } - send(data: string): Promise { + send(data: any): Promise { this.webSocket.send(data); return Promise.resolve(); } @@ -37,4 +33,7 @@ class WebSocketTransport implements ITransport { stop(): void { this.webSocket.close(); } + + onDataReceived: DataReceived; + onError: ErrorHandler; } \ No newline at end of file