Add TSLint rule file and fix all violations (#1381)
This commit is contained in:
parent
f08da32eb2
commit
331bf3515d
|
|
@ -10,7 +10,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"clean": "node ../node_modules/rimraf/bin.js ./wwwroot/dist",
|
||||
"build": "npm run build:tsc && npm run build:rollup",
|
||||
"build": "npm run build:lint && npm run build:tsc && npm run build:rollup",
|
||||
"build:lint": "node ../node_modules/tslint/bin/tslint -c ../tslint.json -p ./tsconfig.json",
|
||||
"build:tsc": "node ../node_modules/typescript/bin/tsc --project ./tsconfig.json",
|
||||
"build:rollup": "node ../node_modules/rollup/bin/rollup -c"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { TransportType, IHubProtocol, JsonHubProtocol } from "@aspnet/signalr"
|
||||
import { MessagePackHubProtocol } from "@aspnet/signalr-protocol-msgpack"
|
||||
import { IHubProtocol, JsonHubProtocol, TransportType } from "@aspnet/signalr";
|
||||
import { MessagePackHubProtocol } from "@aspnet/signalr-protocol-msgpack";
|
||||
|
||||
export const ECHOENDPOINT_URL = "http://" + document.location.host + "/echo";
|
||||
|
||||
export function getTransportTypes(): TransportType[] {
|
||||
var transportTypes = [];
|
||||
const transportTypes = [];
|
||||
if (typeof WebSocket !== "undefined") {
|
||||
transportTypes.push(TransportType.WebSockets);
|
||||
}
|
||||
|
|
@ -20,13 +20,13 @@ export function getTransportTypes(): TransportType[] {
|
|||
}
|
||||
|
||||
export function eachTransport(action: (transport: TransportType) => void) {
|
||||
getTransportTypes().forEach(function (t) {
|
||||
getTransportTypes().forEach((t) => {
|
||||
return action(t);
|
||||
});
|
||||
}
|
||||
|
||||
export function eachTransportAndProtocol(action: (transport: TransportType, protocol: IHubProtocol) => void) {
|
||||
var protocols : IHubProtocol[] = [new JsonHubProtocol()];
|
||||
const protocols: IHubProtocol[] = [new JsonHubProtocol()];
|
||||
// IE9 does not support XmlHttpRequest advanced features so disable for now
|
||||
// This can be enabled if we fix: https://github.com/aspnet/SignalR/issues/742
|
||||
if (typeof new XMLHttpRequest().responseType === "string") {
|
||||
|
|
@ -35,9 +35,9 @@ export function eachTransportAndProtocol(action: (transport: TransportType, prot
|
|||
// Everything works fine in the module
|
||||
protocols.push(new MessagePackHubProtocol());
|
||||
}
|
||||
getTransportTypes().forEach(function (t) {
|
||||
return protocols.forEach(function (p) {
|
||||
getTransportTypes().forEach((t) => {
|
||||
return protocols.forEach((p) => {
|
||||
return action(t, p);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,63 +1,63 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { HttpConnection, LogLevel, TransportType } from "@aspnet/signalr"
|
||||
import { eachTransport, ECHOENDPOINT_URL } from "./Common"
|
||||
import { HttpConnection, LogLevel, TransportType } from "@aspnet/signalr";
|
||||
import { eachTransport, ECHOENDPOINT_URL } from "./Common";
|
||||
|
||||
describe('connection', function () {
|
||||
if (typeof WebSocket !== 'undefined') {
|
||||
it("can connect to the server without specifying transport explicitly", function (done) {
|
||||
var message = "Hello World!";
|
||||
var connection = new HttpConnection(ECHOENDPOINT_URL);
|
||||
describe("connection", () => {
|
||||
if (typeof WebSocket !== "undefined") {
|
||||
it("can connect to the server without specifying transport explicitly", (done) => {
|
||||
const message = "Hello World!";
|
||||
const connection = new HttpConnection(ECHOENDPOINT_URL);
|
||||
|
||||
var received = "";
|
||||
connection.onreceive = function (data) {
|
||||
let received = "";
|
||||
connection.onreceive = (data) => {
|
||||
received += data;
|
||||
if (data == message) {
|
||||
if (data === message) {
|
||||
connection.stop();
|
||||
}
|
||||
};
|
||||
|
||||
connection.onclose = function (error) {
|
||||
connection.onclose = (error) => {
|
||||
expect(error).toBeUndefined();
|
||||
done();
|
||||
};
|
||||
|
||||
connection.start().then(function () {
|
||||
connection.start().then(() => {
|
||||
connection.send(message);
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail();
|
||||
done();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
eachTransport(function (transportType) {
|
||||
it("over " + TransportType[transportType] + " can send and receive messages", function (done) {
|
||||
var message = "Hello World!";
|
||||
eachTransport((transportType) => {
|
||||
it("over " + TransportType[transportType] + " can send and receive messages", (done) => {
|
||||
const message = "Hello World!";
|
||||
// the url should be resolved relative to the document.location.host
|
||||
// and the leading '/' should be automatically added to the url
|
||||
var connection = new HttpConnection("echo", {
|
||||
const connection = new HttpConnection("echo", {
|
||||
logger: LogLevel.Trace,
|
||||
transport: transportType,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
var received = "";
|
||||
connection.onreceive = function (data) {
|
||||
let received = "";
|
||||
connection.onreceive = (data) => {
|
||||
received += data;
|
||||
if (data == message) {
|
||||
if (data === message) {
|
||||
connection.stop();
|
||||
}
|
||||
};
|
||||
|
||||
connection.onclose = function (error) {
|
||||
connection.onclose = (error) => {
|
||||
expect(error).toBeUndefined();
|
||||
done();
|
||||
};
|
||||
|
||||
connection.start().then(function () {
|
||||
connection.start().then(() => {
|
||||
connection.send(message);
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail();
|
||||
done();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,394 +1,394 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { TransportType, HubConnection, LogLevel } from "@aspnet/signalr";
|
||||
import { HubConnection, LogLevel, TransportType } from "@aspnet/signalr";
|
||||
|
||||
import { eachTransportAndProtocol, eachTransport } from "./Common";
|
||||
import { eachTransport, eachTransportAndProtocol } from "./Common";
|
||||
|
||||
var TESTHUBENDPOINT_URL = '/testhub';
|
||||
const TESTHUBENDPOINT_URL = "/testhub";
|
||||
|
||||
describe('hubConnection', function () {
|
||||
eachTransportAndProtocol(function (transportType, protocol) {
|
||||
describe(protocol.name + ' over ' + TransportType[transportType] + ' transport', function () {
|
||||
it('can invoke server method and receive result', function (done) {
|
||||
var message = '你好,世界!';
|
||||
describe("hubConnection", () => {
|
||||
eachTransportAndProtocol((transportType, protocol) => {
|
||||
describe(protocol.name + " over " + TransportType[transportType] + " transport", () => {
|
||||
it("can invoke server method and receive result", (done) => {
|
||||
const message = "你好,世界!";
|
||||
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.onclose((error) => {
|
||||
expect(error).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.invoke('Echo', message).then(function (result) {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.invoke("Echo", message).then((result) => {
|
||||
expect(result).toBe(message);
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
}).then(function () {
|
||||
}).then(() => {
|
||||
hubConnection.stop();
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can invoke server method non-blocking and not receive result', function (done) {
|
||||
var message = '你好,世界!';
|
||||
it("can invoke server method non-blocking and not receive result", (done) => {
|
||||
const message = "你好,世界!";
|
||||
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.onclose((error) => {
|
||||
expect(error).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.send('Echo', message).catch(function (e) {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.send("Echo", message).catch((e) => {
|
||||
fail(e);
|
||||
}).then(function () {
|
||||
}).then(() => {
|
||||
hubConnection.stop();
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can invoke server method structural object and receive structural result', function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("can invoke server method structural object and receive structural result", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
hubConnection.on('CustomObject', function (customObject) {
|
||||
expect(customObject.Name).toBe('test');
|
||||
hubConnection.on("CustomObject", (customObject) => {
|
||||
expect(customObject.Name).toBe("test");
|
||||
expect(customObject.Value).toBe(42);
|
||||
hubConnection.stop();
|
||||
});
|
||||
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.onclose((error) => {
|
||||
expect(error).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.send('SendCustomObject', { Name: 'test', Value: 42 });
|
||||
}).catch(function (e) {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.send("SendCustomObject", { Name: "test", Value: 42 });
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can stream server method and receive result', function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("can stream server method and receive result", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.onclose((error) => {
|
||||
expect(error).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
|
||||
var received = [];
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.stream('Stream').subscribe({
|
||||
next: function next(item) {
|
||||
received.push(item);
|
||||
const received = [];
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.stream("Stream").subscribe({
|
||||
complete: function complete() {
|
||||
expect(received).toEqual(["a", "b", "c"]);
|
||||
hubConnection.stop();
|
||||
},
|
||||
error: function error(err) {
|
||||
fail(err);
|
||||
hubConnection.stop();
|
||||
},
|
||||
complete: function complete() {
|
||||
expect(received).toEqual(['a', 'b', 'c']);
|
||||
hubConnection.stop();
|
||||
}
|
||||
next: function next(item) {
|
||||
received.push(item);
|
||||
},
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rethrows an exception from the server when invoking', function (done) {
|
||||
var errorMessage = 'An error occurred.';
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("rethrows an exception from the server when invoking", (done) => {
|
||||
const errorMessage = "An error occurred.";
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.invoke('ThrowException', errorMessage).then(function () {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.invoke("ThrowException", errorMessage).then(() => {
|
||||
// exception expected but none thrown
|
||||
fail();
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
expect(e.message).toBe(errorMessage);
|
||||
}).then(function () {
|
||||
}).then(() => {
|
||||
return hubConnection.stop();
|
||||
}).then(function () {
|
||||
}).then(() => {
|
||||
done();
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an exception when invoking streaming method with invoke', function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("throws an exception when invoking streaming method with invoke", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.invoke('EmptyStream').then(function () {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.invoke("EmptyStream").then(() => {
|
||||
// exception expected but none thrown
|
||||
fail();
|
||||
}).catch(function (e) {
|
||||
expect(e.message).toBe('The client attempted to invoke the streaming \'EmptyStream\' method in a non-streaming fashion.');
|
||||
}).then(function () {
|
||||
}).catch((e) => {
|
||||
expect(e.message).toBe("The client attempted to invoke the streaming 'EmptyStream' method in a non-streaming fashion.");
|
||||
}).then(() => {
|
||||
return hubConnection.stop();
|
||||
}).then(function () {
|
||||
}).then(() => {
|
||||
done();
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an exception when receiving a streaming result for method called with invoke', function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("throws an exception when receiving a streaming result for method called with invoke", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.invoke('Stream').then(function () {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.invoke("Stream").then(() => {
|
||||
// exception expected but none thrown
|
||||
fail();
|
||||
}).catch(function (e) {
|
||||
expect(e.message).toBe('The client attempted to invoke the streaming \'Stream\' method in a non-streaming fashion.');
|
||||
}).then(function () {
|
||||
}).catch((e) => {
|
||||
expect(e.message).toBe("The client attempted to invoke the streaming 'Stream' method in a non-streaming fashion.");
|
||||
}).then(() => {
|
||||
return hubConnection.stop();
|
||||
}).then(function () {
|
||||
}).then(() => {
|
||||
done();
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rethrows an exception from the server when streaming', function (done) {
|
||||
var errorMessage = 'An error occurred.';
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("rethrows an exception from the server when streaming", (done) => {
|
||||
const errorMessage = "An error occurred.";
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.stream('StreamThrowException', errorMessage).subscribe({
|
||||
next: function next(item) {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.stream("StreamThrowException", errorMessage).subscribe({
|
||||
complete: function complete() {
|
||||
hubConnection.stop();
|
||||
fail();
|
||||
},
|
||||
error: function error(err) {
|
||||
expect(err.message).toEqual('An error occurred.');
|
||||
expect(err.message).toEqual("An error occurred.");
|
||||
hubConnection.stop();
|
||||
done();
|
||||
},
|
||||
complete: function complete() {
|
||||
hubConnection.stop();
|
||||
fail();
|
||||
}
|
||||
});
|
||||
}).catch(function (e) {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an exception when invoking hub method with stream', function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.stream('Echo', '42').subscribe({
|
||||
next: function next(item) {
|
||||
hubConnection.stop();
|
||||
fail();
|
||||
},
|
||||
error: function error(err) {
|
||||
expect(err.message).toEqual('The client attempted to invoke the non-streaming \'Echo\' method in a streaming fashion.');
|
||||
hubConnection.stop();
|
||||
done();
|
||||
},
|
||||
complete: function complete() {
|
||||
hubConnection.stop();
|
||||
fail();
|
||||
}
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can receive server calls', function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("throws an exception when invoking hub method with stream", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
var message = '你好 SignalR!';
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.stream("Echo", "42").subscribe({
|
||||
complete: function complete() {
|
||||
hubConnection.stop();
|
||||
fail();
|
||||
},
|
||||
error: function error(err) {
|
||||
expect(err.message).toEqual("The client attempted to invoke the non-streaming 'Echo' method in a streaming fashion.");
|
||||
hubConnection.stop();
|
||||
done();
|
||||
},
|
||||
next: function next(item) {
|
||||
hubConnection.stop();
|
||||
fail();
|
||||
},
|
||||
});
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("can receive server calls", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
});
|
||||
|
||||
const message = "你好 SignalR!";
|
||||
|
||||
// client side method names are case insensitive
|
||||
var methodName = 'message';
|
||||
var idx = Math.floor(Math.random() * (methodName.length - 1));
|
||||
let methodName = "message";
|
||||
const idx = Math.floor(Math.random() * (methodName.length - 1));
|
||||
methodName = methodName.substr(0, idx) + methodName[idx].toUpperCase() + methodName.substr(idx + 1);
|
||||
|
||||
hubConnection.on(methodName, function (msg) {
|
||||
hubConnection.on(methodName, (msg) => {
|
||||
expect(msg).toBe(message);
|
||||
done();
|
||||
});
|
||||
|
||||
hubConnection.start()
|
||||
.then(function () {
|
||||
return hubConnection.invoke('InvokeWithString', message);
|
||||
.then(() => {
|
||||
return hubConnection.invoke("InvokeWithString", message);
|
||||
})
|
||||
.then(function () {
|
||||
.then(() => {
|
||||
return hubConnection.stop();
|
||||
})
|
||||
.catch(function (e) {
|
||||
.catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can receive server calls without rebinding handler when restarted', function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("can receive server calls without rebinding handler when restarted", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
var message = '你好 SignalR!';
|
||||
const message = "你好 SignalR!";
|
||||
|
||||
// client side method names are case insensitive
|
||||
var methodName = 'message';
|
||||
var idx = Math.floor(Math.random() * (methodName.length - 1));
|
||||
let methodName = "message";
|
||||
const idx = Math.floor(Math.random() * (methodName.length - 1));
|
||||
methodName = methodName.substr(0, idx) + methodName[idx].toUpperCase() + methodName.substr(idx + 1);
|
||||
|
||||
let closeCount = 0;
|
||||
let invocationCount = 0;
|
||||
|
||||
hubConnection.onclose(function (e) {
|
||||
hubConnection.onclose((e) => {
|
||||
expect(e).toBeUndefined();
|
||||
closeCount += 1;
|
||||
if (closeCount === 1) {
|
||||
// Reconnect
|
||||
hubConnection.start()
|
||||
.then(function () {
|
||||
return hubConnection.invoke('InvokeWithString', message);
|
||||
.then(() => {
|
||||
return hubConnection.invoke("InvokeWithString", message);
|
||||
})
|
||||
.then(function () {
|
||||
.then(() => {
|
||||
return hubConnection.stop();
|
||||
})
|
||||
.catch(function (e) {
|
||||
fail(e);
|
||||
.catch((error) => {
|
||||
fail(error);
|
||||
done();
|
||||
});
|
||||
} else {
|
||||
expect(invocationCount).toBe(2);
|
||||
done();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
hubConnection.on(methodName, function (msg) {
|
||||
hubConnection.on(methodName, (msg) => {
|
||||
expect(msg).toBe(message);
|
||||
invocationCount += 1;
|
||||
});
|
||||
|
||||
hubConnection.start()
|
||||
.then(function () {
|
||||
return hubConnection.invoke('InvokeWithString', message);
|
||||
.then(() => {
|
||||
return hubConnection.invoke("InvokeWithString", message);
|
||||
})
|
||||
.then(function () {
|
||||
.then(() => {
|
||||
return hubConnection.stop();
|
||||
})
|
||||
.catch(function (e) {
|
||||
.catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('closed with error if hub cannot be created', function (done) {
|
||||
var errorRegex = {
|
||||
WebSockets: '1011|1005', // Message is browser specific (e.g. 'Websocket closed with status code: 1011'), Edge and IE report 1005 even though the server sent 1011
|
||||
LongPolling: 'Internal Server Error',
|
||||
ServerSentEvents: 'Error occurred'
|
||||
it("closed with error if hub cannot be created", (done) => {
|
||||
const errorRegex = {
|
||||
LongPolling: "Internal Server Error",
|
||||
ServerSentEvents: "Error occurred",
|
||||
WebSockets: "1011|1005", // Message is browser specific (e.g. 'Websocket closed with status code: 1011'), Edge and IE report 1005 even though the server sent 1011
|
||||
};
|
||||
|
||||
var hubConnection = new HubConnection('http://' + document.location.host + '/uncreatable', {
|
||||
const hubConnection = new HubConnection("http://" + document.location.host + "/uncreatable", {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.onclose((error) => {
|
||||
expect(error.message).toMatch(errorRegex[TransportType[transportType]]);
|
||||
done();
|
||||
});
|
||||
hubConnection.start();
|
||||
});
|
||||
|
||||
it('can handle different types', function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
it("can handle different types", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.onclose((error) => {
|
||||
expect(error).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
|
||||
var complexObject = {
|
||||
String: 'Hello, World!',
|
||||
IntArray: [0x01, 0x02, 0x03, 0xff],
|
||||
const complexObject = {
|
||||
ByteArray: protocol.name === "json"
|
||||
? "aGVsbG8="
|
||||
: new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x6f]),
|
||||
GUID: protocol.name === "json"
|
||||
? "00010203-0405-0607-0706-050403020100"
|
||||
: new Uint8Array([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00])
|
||||
: new Uint8Array([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00]),
|
||||
IntArray: [0x01, 0x02, 0x03, 0xff],
|
||||
String: "Hello, World!",
|
||||
};
|
||||
|
||||
hubConnection.start()
|
||||
.then(function () {
|
||||
return hubConnection.invoke('EchoComplexObject', complexObject);
|
||||
.then(() => {
|
||||
return hubConnection.invoke("EchoComplexObject", complexObject);
|
||||
})
|
||||
.then(function (value) {
|
||||
.then((value) => {
|
||||
if (protocol.name === "messagepack") {
|
||||
// msgpack creates a Buffer for byte arrays and jasmine fails to compare a Buffer
|
||||
// and a Uint8Array even though Buffer instances are also Uint8Array instances
|
||||
|
|
@ -398,7 +398,7 @@ describe('hubConnection', function () {
|
|||
// be extracted. Note that with msgpack5 the original bytes will be encoded with utf8
|
||||
// and needs to be decoded. To not go into utf8 encoding intricacies the test uses values
|
||||
// less than 0x80.
|
||||
let guidBytes = [];
|
||||
const guidBytes = [];
|
||||
for (let i = 0; i < value.GUID.length; i++) {
|
||||
guidBytes.push(value.GUID.charCodeAt(i));
|
||||
}
|
||||
|
|
@ -406,40 +406,40 @@ describe('hubConnection', function () {
|
|||
}
|
||||
expect(value).toEqual(complexObject);
|
||||
})
|
||||
.then(function () {
|
||||
.then(() => {
|
||||
hubConnection.stop();
|
||||
})
|
||||
.catch(function (e) {
|
||||
.catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can be restarted', function (done) {
|
||||
var message = '你好,世界!';
|
||||
it("can be restarted", (done) => {
|
||||
const message = "你好,世界!";
|
||||
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
protocol,
|
||||
transport: transportType,
|
||||
protocol: protocol,
|
||||
logger: LogLevel.Trace
|
||||
});
|
||||
|
||||
let closeCount = 0;
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.onclose((error) => {
|
||||
expect(error).toBe(undefined);
|
||||
|
||||
// Start and invoke again
|
||||
if (closeCount === 0) {
|
||||
closeCount += 1;
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.invoke('Echo', message).then(function (result) {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.invoke("Echo", message).then((result) => {
|
||||
expect(result).toBe(message);
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
}).then(function () {
|
||||
hubConnection.stop()
|
||||
}).then(() => {
|
||||
hubConnection.stop();
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
|
|
@ -448,15 +448,15 @@ describe('hubConnection', function () {
|
|||
}
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.invoke('Echo', message).then(function (result) {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.invoke("Echo", message).then((result) => {
|
||||
expect(result).toBe(message);
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
}).then(function () {
|
||||
hubConnection.stop()
|
||||
}).then(() => {
|
||||
hubConnection.stop();
|
||||
});
|
||||
}).catch(function (e) {
|
||||
}).catch((e) => {
|
||||
fail(e);
|
||||
done();
|
||||
});
|
||||
|
|
@ -464,53 +464,53 @@ describe('hubConnection', function () {
|
|||
});
|
||||
});
|
||||
|
||||
eachTransport(function (transportType) {
|
||||
describe(' over ' + TransportType[transportType] + ' transport', function () {
|
||||
eachTransport((transportType) => {
|
||||
describe(" over " + TransportType[transportType] + " transport", () => {
|
||||
|
||||
it('can connect to hub with authorization', async function (done) {
|
||||
var message = '你好,世界!';
|
||||
it("can connect to hub with authorization", async (done) => {
|
||||
const message = "你好,世界!";
|
||||
|
||||
var hubConnection;
|
||||
getJwtToken('http://' + document.location.host + '/generateJwtToken')
|
||||
.then(jwtToken => {
|
||||
hubConnection = new HubConnection('/authorizedhub', {
|
||||
transport: transportType,
|
||||
let hubConnection;
|
||||
getJwtToken("http://" + document.location.host + "/generateJwtToken")
|
||||
.then((jwtToken) => {
|
||||
hubConnection = new HubConnection("/authorizedhub", {
|
||||
accessTokenFactory: () => jwtToken,
|
||||
logger: LogLevel.Trace,
|
||||
accessTokenFactory: () => jwtToken
|
||||
transport: transportType,
|
||||
});
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.onclose((error) => {
|
||||
expect(error).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
return hubConnection.start();
|
||||
})
|
||||
.then(() => {
|
||||
return hubConnection.invoke('Echo', message)
|
||||
return hubConnection.invoke("Echo", message);
|
||||
})
|
||||
.then(response => {
|
||||
.then((response) => {
|
||||
expect(response).toEqual(message);
|
||||
done();
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
fail(err);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
if (transportType != TransportType.LongPolling) {
|
||||
it("terminates if no messages received within timeout interval", function (done) {
|
||||
var hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
transport: transportType,
|
||||
if (transportType !== TransportType.LongPolling) {
|
||||
it("terminates if no messages received within timeout interval", (done) => {
|
||||
const hubConnection = new HubConnection(TESTHUBENDPOINT_URL, {
|
||||
logger: LogLevel.Trace,
|
||||
timeoutInMilliseconds: 100
|
||||
timeoutInMilliseconds: 100,
|
||||
transport: transportType,
|
||||
});
|
||||
|
||||
var timeout = setTimeout(200, function () {
|
||||
const timeout = setTimeout(200, () => {
|
||||
fail("Server timeout did not fire within expected interval");
|
||||
});
|
||||
|
||||
hubConnection.start().then(function () {
|
||||
hubConnection.onclose(function (error) {
|
||||
hubConnection.start().then(() => {
|
||||
hubConnection.onclose((error) => {
|
||||
clearTimeout(timeout);
|
||||
expect(error).toEqual(new Error("Server timeout elapsed without receiving a message from the server."));
|
||||
done();
|
||||
|
|
@ -521,25 +521,24 @@ describe('hubConnection', function () {
|
|||
});
|
||||
});
|
||||
|
||||
function getJwtToken(url) : Promise<string> {
|
||||
function getJwtToken(url): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open('GET', url, true);
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
xhr.open("GET", url, true);
|
||||
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
xhr.send();
|
||||
xhr.onload = () => {
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resolve(xhr.response || xhr.responseText);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
reject(new Error(xhr.statusText));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
reject(new Error(xhr.statusText));
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,26 +3,26 @@
|
|||
|
||||
import { ECHOENDPOINT_URL } from "./Common";
|
||||
|
||||
if (typeof WebSocket !== 'undefined') {
|
||||
describe('WebSockets', function () {
|
||||
it('can be used to connect to SignalR', function (done) {
|
||||
var message = "message";
|
||||
if (typeof WebSocket !== "undefined") {
|
||||
describe("WebSockets", () => {
|
||||
it("can be used to connect to SignalR", (done) => {
|
||||
const message = "message";
|
||||
|
||||
var webSocket = new WebSocket(ECHOENDPOINT_URL.replace(/^http/, "ws"));
|
||||
const webSocket = new WebSocket(ECHOENDPOINT_URL.replace(/^http/, "ws"));
|
||||
|
||||
webSocket.onopen = function () {
|
||||
webSocket.onopen = () => {
|
||||
webSocket.send(message);
|
||||
};
|
||||
|
||||
var received = "";
|
||||
webSocket.onmessage = function (event) {
|
||||
let received = "";
|
||||
webSocket.onmessage = (event) => {
|
||||
received += event.data;
|
||||
if (received === message) {
|
||||
webSocket.close();
|
||||
}
|
||||
};
|
||||
|
||||
webSocket.onclose = function (event) {
|
||||
webSocket.onclose = (event) => {
|
||||
if (!event.wasClean) {
|
||||
fail("connection closed with unexpected status code: " + event.code + " " + event.reason);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import 'es6-promise/dist/es6-promise.auto.js';
|
||||
import "es6-promise/dist/es6-promise.auto.js";
|
||||
|
||||
import './ConnectionTests'
|
||||
import './HubConnectionTests'
|
||||
import './WebSocketTests'
|
||||
import "./ConnectionTests";
|
||||
import "./HubConnectionTests";
|
||||
import "./WebSocketTests";
|
||||
|
|
|
|||
|
|
@ -16,6 +16,27 @@
|
|||
"integrity": "sha512-KA4GKOpgXnrqEH2eCVhiv2CsxgXGQJgV1X0vsGlh+WCnxbeAE1GT44ZsTU1IN5dEeV/gDupKa7gWo08V5IxWVQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
|
||||
"dev": true
|
||||
},
|
||||
"argparse": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz",
|
||||
"integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sprintf-js": "1.0.3"
|
||||
}
|
||||
},
|
||||
"arr-diff": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz",
|
||||
|
|
@ -43,6 +64,32 @@
|
|||
"integrity": "sha1-GcenYEc3dEaPILLS0DNyrX1Mv10=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "1.1.3",
|
||||
"esutils": "2.0.2",
|
||||
"js-tokens": "3.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "2.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"has-ansi": "2.0.0",
|
||||
"strip-ansi": "3.0.1",
|
||||
"supports-color": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
|
|
@ -76,6 +123,58 @@
|
|||
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.3.0.tgz",
|
||||
"integrity": "sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "3.2.0",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"supports-color": "4.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "1.9.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.5.0.tgz",
|
||||
"integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
|
||||
"integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
|
||||
"dev": true
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.14.0.tgz",
|
||||
"integrity": "sha512-okPpdvdJr6mUGi2XzupC+irQxzwGLVaBzacFC14hjLv8NColXEsxsU+QaeuSSXpQUak5g2K0vQ7WjA1e8svczg==",
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
|
|
@ -88,12 +187,36 @@
|
|||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
|
||||
"dev": true
|
||||
},
|
||||
"diff": {
|
||||
"version": "3.4.0",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-3.4.0.tgz",
|
||||
"integrity": "sha512-QpVuMTEoJMF7cKzi6bvWhRulU1fZqZnvyVQgNhPaxxuTYwyjn/j1v9falseQ/uXWwPnO56RBfwtg4h/EQXmucA==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
},
|
||||
"esprima": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.0.tgz",
|
||||
"integrity": "sha512-oftTcaMu/EGrEIu904mWteKIv8vMuOgGYo7EhVJJN00R/EED9DCua/xxHRdYnKtcECzVg7xOWhflvJMnqcFZjw==",
|
||||
"dev": true
|
||||
},
|
||||
"estree-walker": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz",
|
||||
"integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=",
|
||||
"dev": true
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
},
|
||||
"exit": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
|
||||
|
|
@ -200,6 +323,21 @@
|
|||
"is-glob": "2.0.1"
|
||||
}
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
|
||||
"integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
|
|
@ -317,6 +455,22 @@
|
|||
"integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=",
|
||||
"dev": true
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
|
||||
"dev": true
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.10.0.tgz",
|
||||
"integrity": "sha512-O2v52ffjLa9VeM43J4XocZE//WT9N0IiwDa3KSHH7Tu8CtH+1qM8SIZvnsTh6v+4yFy5KUY3BHUVwjpfAWsjIA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"argparse": "1.0.9",
|
||||
"esprima": "4.0.0"
|
||||
}
|
||||
},
|
||||
"kind-of": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
|
||||
|
|
@ -579,6 +733,12 @@
|
|||
"micromatch": "2.3.11"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
"integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-resolve": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.1.tgz",
|
||||
|
|
@ -598,6 +758,62 @@
|
|||
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
|
||||
"dev": true
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz",
|
||||
"integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==",
|
||||
"dev": true
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.9.1",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz",
|
||||
"integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-code-frame": "6.26.0",
|
||||
"builtin-modules": "1.1.1",
|
||||
"chalk": "2.3.0",
|
||||
"commander": "2.14.0",
|
||||
"diff": "3.4.0",
|
||||
"glob": "7.1.2",
|
||||
"js-yaml": "3.10.0",
|
||||
"minimatch": "3.0.4",
|
||||
"resolve": "1.5.0",
|
||||
"semver": "5.5.0",
|
||||
"tslib": "1.9.0",
|
||||
"tsutils": "2.21.0"
|
||||
}
|
||||
},
|
||||
"tsutils": {
|
||||
"version": "2.21.0",
|
||||
"resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.21.0.tgz",
|
||||
"integrity": "sha512-zlOHTYtTwvTiKxUyAU8wiKzPpAgwZrGjb7AY18VUlxuCgBiTMVorIl5HjrCT8V64Hm34RI1BZITJMVQpBLMxVg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tslib": "1.9.0"
|
||||
}
|
||||
},
|
||||
"typescript": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.7.1.tgz",
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
"rollup-plugin-commonjs": "^8.2.6",
|
||||
"rollup-plugin-node-resolve": "^3.0.2",
|
||||
"rollup-plugin-sourcemaps": "^0.4.2",
|
||||
"tslint": "^5.9.1",
|
||||
"typescript": "^2.7.1",
|
||||
"uglify-js": "^3.3.5"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@aspnet/signalr-protocol-msgpack",
|
||||
"version": "1.0.0-preview1-t000",
|
||||
"version": "1.0.0-preview2-t000",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"clean": "node ../node_modules/rimraf/bin.js ./dist",
|
||||
"build": "npm run clean && npm run build:esm && npm run build:cjs && npm run build:browser && npm run build:uglify",
|
||||
"build": "npm run clean && npm run build:lint && npm run build:esm && npm run build:cjs && npm run build:browser && npm run build:uglify",
|
||||
"build:lint": "node ../node_modules/tslint/bin/tslint -c ../tslint.json -p ./tsconfig.json",
|
||||
"build:esm": "node ../node_modules/typescript/bin/tsc --project ./tsconfig.json --module es2015 --outDir ./dist/esm --target ES2015 -d",
|
||||
"build:cjs": "node ../node_modules/typescript/bin/tsc --project ./tsconfig.json --module commonjs --outDir ./dist/cjs --target ES5",
|
||||
"build:browser": "node ../node_modules/rollup/bin/rollup -c",
|
||||
|
|
|
|||
|
|
@ -6,13 +6,12 @@ export class BinaryMessageFormat {
|
|||
// The length prefix of binary messages is encoded as VarInt. Read the comment in
|
||||
// the BinaryMessageParser.TryParseMessage for details.
|
||||
|
||||
static write(output: Uint8Array): ArrayBuffer {
|
||||
public static write(output: Uint8Array): ArrayBuffer {
|
||||
// msgpack5 uses returns Buffer instead of Uint8Array on IE10 and some other browser
|
||||
// in which case .byteLength does will be undefined
|
||||
let size = output.byteLength || output.length;
|
||||
let lenBuffer = [];
|
||||
do
|
||||
{
|
||||
const lenBuffer = [];
|
||||
do {
|
||||
let sizePart = size & 0x7f;
|
||||
size = size >> 7;
|
||||
if (size > 0) {
|
||||
|
|
@ -26,15 +25,15 @@ export class BinaryMessageFormat {
|
|||
// in which case .byteLength does will be undefined
|
||||
size = output.byteLength || output.length;
|
||||
|
||||
let buffer = new Uint8Array(lenBuffer.length + size);
|
||||
const buffer = new Uint8Array(lenBuffer.length + size);
|
||||
buffer.set(lenBuffer, 0);
|
||||
buffer.set(output, lenBuffer.length);
|
||||
return buffer.buffer;
|
||||
}
|
||||
|
||||
static parse(input: ArrayBuffer): Uint8Array[] {
|
||||
let result: Uint8Array[] = [];
|
||||
let uint8Array = new Uint8Array(input);
|
||||
public static parse(input: ArrayBuffer): Uint8Array[] {
|
||||
const result: Uint8Array[] = [];
|
||||
const uint8Array = new Uint8Array(input);
|
||||
const maxLengthPrefixSize = 5;
|
||||
const numBitsToShift = [0, 7, 14, 21, 28 ];
|
||||
|
||||
|
|
@ -42,13 +41,12 @@ export class BinaryMessageFormat {
|
|||
let numBytes = 0;
|
||||
let size = 0;
|
||||
let byteRead;
|
||||
do
|
||||
{
|
||||
do {
|
||||
byteRead = uint8Array[offset + numBytes];
|
||||
size = size | ((byteRead & 0x7f) << (numBitsToShift[numBytes]));
|
||||
numBytes++;
|
||||
}
|
||||
while (numBytes < Math.min(maxLengthPrefixSize, input.byteLength - offset) && (byteRead & 0x80) != 0);
|
||||
while (numBytes < Math.min(maxLengthPrefixSize, input.byteLength - offset) && (byteRead & 0x80) !== 0);
|
||||
|
||||
if ((byteRead & 0x80) !== 0 && numBytes < maxLengthPrefixSize) {
|
||||
throw new Error("Cannot read message size.");
|
||||
|
|
@ -63,8 +61,7 @@ export class BinaryMessageFormat {
|
|||
result.push(uint8Array.slice
|
||||
? uint8Array.slice(offset + numBytes, offset + numBytes + size)
|
||||
: uint8Array.subarray(offset + numBytes, offset + numBytes + size));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
throw new Error("Incomplete message.");
|
||||
}
|
||||
|
||||
|
|
@ -73,4 +70,4 @@ export class BinaryMessageFormat {
|
|||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { IHubProtocol, ProtocolType, MessageType, HubMessage, InvocationMessage, StreamItemMessage, CompletionMessage, StreamInvocationMessage, MessageHeaders } from "@aspnet/signalr";
|
||||
import { BinaryMessageFormat } from "./BinaryMessageFormat"
|
||||
import { Buffer } from 'buffer';
|
||||
import { CompletionMessage, HubMessage, IHubProtocol, InvocationMessage, MessageHeaders, MessageType, ProtocolType, StreamInvocationMessage, StreamItemMessage } from "@aspnet/signalr";
|
||||
import { Buffer } from "buffer";
|
||||
import * as msgpack5 from "msgpack5";
|
||||
import { BinaryMessageFormat } from "./BinaryMessageFormat";
|
||||
|
||||
export class MessagePackHubProtocol implements IHubProtocol {
|
||||
|
||||
readonly name: string = "messagepack";
|
||||
public readonly name: string = "messagepack";
|
||||
|
||||
readonly type: ProtocolType = ProtocolType.Binary;
|
||||
public readonly type: ProtocolType = ProtocolType.Binary;
|
||||
|
||||
parseMessages(input: ArrayBuffer): HubMessage[] {
|
||||
return BinaryMessageFormat.parse(input).map(m => this.parseMessage(m));
|
||||
public parseMessages(input: ArrayBuffer): HubMessage[] {
|
||||
return BinaryMessageFormat.parse(input).map((m) => this.parseMessage(m));
|
||||
}
|
||||
|
||||
private parseMessage(input: Uint8Array): HubMessage {
|
||||
if (input.length == 0) {
|
||||
if (input.length === 0) {
|
||||
throw new Error("Invalid payload.");
|
||||
}
|
||||
|
||||
let msgpack = msgpack5();
|
||||
let properties = msgpack.decode(new Buffer(input));
|
||||
if (properties.length == 0 || !(properties instanceof Array)) {
|
||||
const msgpack = msgpack5();
|
||||
const properties = msgpack.decode(new Buffer(input));
|
||||
if (properties.length === 0 || !(properties instanceof Array)) {
|
||||
throw new Error("Invalid payload.");
|
||||
}
|
||||
|
||||
let messageType = properties[0] as MessageType;
|
||||
const messageType = properties[0] as MessageType;
|
||||
|
||||
switch (messageType) {
|
||||
case MessageType.Invocation:
|
||||
|
|
@ -44,52 +44,51 @@ export class MessagePackHubProtocol implements IHubProtocol {
|
|||
}
|
||||
|
||||
private createPingMessage(properties: any[]): HubMessage {
|
||||
if (properties.length != 1) {
|
||||
if (properties.length !== 1) {
|
||||
throw new Error("Invalid payload for Ping message.");
|
||||
}
|
||||
|
||||
return {
|
||||
// Ping messages have no headers.
|
||||
type: MessageType.Ping
|
||||
type: MessageType.Ping,
|
||||
} as HubMessage;
|
||||
}
|
||||
|
||||
private createInvocationMessage(headers: MessageHeaders, properties: any[]): InvocationMessage {
|
||||
if (properties.length != 5) {
|
||||
if (properties.length !== 5) {
|
||||
throw new Error("Invalid payload for Invocation message.");
|
||||
}
|
||||
|
||||
let invocationId = properties[2] as string;
|
||||
const invocationId = properties[2] as string;
|
||||
if (invocationId) {
|
||||
return {
|
||||
headers,
|
||||
type: MessageType.Invocation,
|
||||
invocationId: invocationId,
|
||||
target: properties[3] as string,
|
||||
arguments: properties[4],
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
headers,
|
||||
invocationId,
|
||||
target: properties[3] as string,
|
||||
type: MessageType.Invocation,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
arguments: properties[4],
|
||||
headers,
|
||||
target: properties[3],
|
||||
arguments: properties[4]
|
||||
type: MessageType.Invocation,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private createStreamItemMessage(headers: MessageHeaders, properties: any[]): StreamItemMessage {
|
||||
if (properties.length != 4) {
|
||||
if (properties.length !== 4) {
|
||||
throw new Error("Invalid payload for stream Result message.");
|
||||
}
|
||||
|
||||
return {
|
||||
headers,
|
||||
type: MessageType.StreamItem,
|
||||
invocationId: properties[2],
|
||||
item: properties[3]
|
||||
item: properties[3],
|
||||
type: MessageType.StreamItem,
|
||||
} as StreamItemMessage;
|
||||
}
|
||||
|
||||
|
|
@ -102,19 +101,19 @@ export class MessagePackHubProtocol implements IHubProtocol {
|
|||
const voidResult = 2;
|
||||
const nonVoidResult = 3;
|
||||
|
||||
let resultKind = properties[3];
|
||||
const resultKind = properties[3];
|
||||
|
||||
if ((resultKind === voidResult && properties.length != 4) ||
|
||||
(resultKind !== voidResult && properties.length != 5)) {
|
||||
if ((resultKind === voidResult && properties.length !== 4) ||
|
||||
(resultKind !== voidResult && properties.length !== 5)) {
|
||||
throw new Error("Invalid payload for Completion message.");
|
||||
}
|
||||
|
||||
let completionMessage = {
|
||||
headers,
|
||||
type: MessageType.Completion,
|
||||
invocationId: properties[2],
|
||||
const completionMessage = {
|
||||
error: null as string,
|
||||
result: null as any
|
||||
headers,
|
||||
invocationId: properties[2],
|
||||
result: null as any,
|
||||
type: MessageType.Completion,
|
||||
};
|
||||
|
||||
switch (resultKind) {
|
||||
|
|
@ -129,7 +128,7 @@ export class MessagePackHubProtocol implements IHubProtocol {
|
|||
return completionMessage as CompletionMessage;
|
||||
}
|
||||
|
||||
writeMessage(message: HubMessage): ArrayBuffer {
|
||||
public writeMessage(message: HubMessage): ArrayBuffer {
|
||||
switch (message.type) {
|
||||
case MessageType.Invocation:
|
||||
return this.writeInvocation(message as InvocationMessage);
|
||||
|
|
@ -144,26 +143,26 @@ export class MessagePackHubProtocol implements IHubProtocol {
|
|||
}
|
||||
|
||||
private writeInvocation(invocationMessage: InvocationMessage): ArrayBuffer {
|
||||
let msgpack = msgpack5();
|
||||
let payload = msgpack.encode([MessageType.Invocation, invocationMessage.headers || {}, invocationMessage.invocationId || null,
|
||||
const msgpack = msgpack5();
|
||||
const payload = msgpack.encode([MessageType.Invocation, invocationMessage.headers || {}, invocationMessage.invocationId || null,
|
||||
invocationMessage.target, invocationMessage.arguments]);
|
||||
|
||||
return BinaryMessageFormat.write(payload.slice());
|
||||
}
|
||||
|
||||
private writeStreamInvocation(streamInvocationMessage: StreamInvocationMessage): ArrayBuffer {
|
||||
let msgpack = msgpack5();
|
||||
let payload = msgpack.encode([MessageType.StreamInvocation, streamInvocationMessage.headers || {}, streamInvocationMessage.invocationId,
|
||||
const msgpack = msgpack5();
|
||||
const payload = msgpack.encode([MessageType.StreamInvocation, streamInvocationMessage.headers || {}, streamInvocationMessage.invocationId,
|
||||
streamInvocationMessage.target, streamInvocationMessage.arguments]);
|
||||
|
||||
return BinaryMessageFormat.write(payload.slice());
|
||||
}
|
||||
|
||||
private readHeaders(properties: any): MessageHeaders {
|
||||
let headers: MessageHeaders = properties[1] as MessageHeaders;
|
||||
const headers: MessageHeaders = properties[1] as MessageHeaders;
|
||||
if (typeof headers !== "object") {
|
||||
throw new Error("Invalid headers.");
|
||||
}
|
||||
return headers;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,4 @@
|
|||
|
||||
// This is where we add any polyfills we'll need for the browser. It is the entry module for browser-specific builds.
|
||||
|
||||
export * from './index'
|
||||
export * from "./index";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
export { MessagePackHubProtocol } from "./MessagePackHubProtocol";
|
||||
export { MessagePackHubProtocol } from "./MessagePackHubProtocol";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@aspnet/signalr",
|
||||
"version": "1.0.0-preview1-t000",
|
||||
"version": "1.0.0-preview2-t000",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"clean": "node ../node_modules/rimraf/bin.js ./dist ./.rpt2_cache",
|
||||
"build": "npm run clean && npm run build:esm && npm run build:cjs && npm run build:browser && npm run build:uglify",
|
||||
"build": "npm run clean && npm run build:lint && npm run build:esm && npm run build:cjs && npm run build:browser && npm run build:uglify",
|
||||
"build:lint": "node ../node_modules/tslint/bin/tslint -c ../tslint.json -p ./tsconfig.json",
|
||||
"build:esm": "node ../node_modules/typescript/bin/tsc --project ./tsconfig.json --module es2015 --outDir ./dist/esm --target ES2015 -d && node ./build/process-dts.js",
|
||||
"build:cjs": "node ../node_modules/typescript/bin/tsc --project ./tsconfig.json --module commonjs --outDir ./dist/cjs --target ES5",
|
||||
"build:browser": "node ../node_modules/rollup/bin/rollup -c",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { asyncit as it } from "./Utils";
|
||||
import { AbortController } from "../src/AbortController";
|
||||
import { asyncit as it } from "./Utils";
|
||||
|
||||
describe("AbortSignal", () => {
|
||||
describe("aborted", () => {
|
||||
|
|
@ -11,21 +11,21 @@ describe("AbortSignal", () => {
|
|||
});
|
||||
|
||||
it("is true when aborted", () => {
|
||||
let controller = new AbortController();
|
||||
let signal = controller.signal;
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
controller.abort();
|
||||
expect(signal.aborted).toBe(true);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe("onabort", () => {
|
||||
it("is called when abort is called", () => {
|
||||
let controller = new AbortController();
|
||||
let signal = controller.signal;
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
let abortCalled = false;
|
||||
signal.onabort = () => abortCalled = true;
|
||||
controller.abort();
|
||||
expect(abortCalled).toBe(true);
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { Base64EncodedHubProtocol } from "../src/Base64EncodedHubProtocol"
|
||||
import { IHubProtocol, HubMessage, ProtocolType } from "../src/IHubProtocol"
|
||||
import { Base64EncodedHubProtocol } from "../src/Base64EncodedHubProtocol";
|
||||
import { HubMessage, IHubProtocol, ProtocolType } from "../src/IHubProtocol";
|
||||
|
||||
class FakeHubProtocol implements IHubProtocol {
|
||||
name: "fakehubprotocol";
|
||||
type: ProtocolType;
|
||||
public name: "fakehubprotocol";
|
||||
public type: ProtocolType;
|
||||
|
||||
parseMessages(input: any): HubMessage[] {
|
||||
public parseMessages(input: any): HubMessage[] {
|
||||
let s = "";
|
||||
|
||||
new Uint8Array(input).forEach((item: any) => {
|
||||
|
|
@ -18,9 +18,9 @@ class FakeHubProtocol implements IHubProtocol {
|
|||
return JSON.parse(s);
|
||||
}
|
||||
|
||||
writeMessage(message: HubMessage): any {
|
||||
let s = JSON.stringify(message);
|
||||
let payload = new Uint8Array(s.length);
|
||||
public writeMessage(message: HubMessage): any {
|
||||
const s = JSON.stringify(message);
|
||||
const payload = new Uint8Array(s.length);
|
||||
for (let i = 0; i < payload.length; i++) {
|
||||
payload[i] = s.charCodeAt(i);
|
||||
}
|
||||
|
|
@ -36,9 +36,9 @@ describe("Base64EncodedHubProtocol", () => {
|
|||
["1.0:A;", new Error("Invalid length: '1.0'")],
|
||||
["2:A;", new Error("Invalid message size.")],
|
||||
["2:ABC;", new Error("Invalid message size.")],
|
||||
] as [string, Error][]).forEach(([payload, expected_error]) => {
|
||||
] as Array<[string, Error]>).forEach(([payload, expectedError]) => {
|
||||
it(`should fail to parse '${payload}'`, () => {
|
||||
expect(() => new Base64EncodedHubProtocol(new FakeHubProtocol()).parseMessages(payload)).toThrow(expected_error);
|
||||
expect(() => new Base64EncodedHubProtocol(new FakeHubProtocol()).parseMessages(payload)).toThrow(expectedError);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -47,10 +47,10 @@ describe("Base64EncodedHubProtocol", () => {
|
|||
] as [[string, any]]).forEach(([payload, message]) => {
|
||||
it(`should be able to parse '${payload}'`, () => {
|
||||
|
||||
let globalAny: any = global;
|
||||
globalAny.atob = function(input: any) { return input };
|
||||
const globalAny: any = global;
|
||||
globalAny.atob = (input: any) => input;
|
||||
|
||||
let result = new Base64EncodedHubProtocol(new FakeHubProtocol()).parseMessages(payload);
|
||||
const result = new Base64EncodedHubProtocol(new FakeHubProtocol()).parseMessages(payload);
|
||||
expect(result).toEqual(message);
|
||||
|
||||
delete globalAny.atob;
|
||||
|
|
@ -59,13 +59,13 @@ describe("Base64EncodedHubProtocol", () => {
|
|||
|
||||
([
|
||||
[{}, "2:{};"],
|
||||
] as [any, string][]).forEach(([message, payload]) => {
|
||||
] as Array<[any, string]>).forEach(([message, payload]) => {
|
||||
it(`should be able to write '${JSON.stringify(message)}'`, () => {
|
||||
|
||||
let globalAny: any = global;
|
||||
globalAny.btoa = function(input: any) { return input };
|
||||
const globalAny: any = global;
|
||||
globalAny.btoa = (input: any) => input;
|
||||
|
||||
let result = new Base64EncodedHubProtocol(new FakeHubProtocol()).writeMessage(message);
|
||||
const result = new Base64EncodedHubProtocol(new FakeHubProtocol()).writeMessage(message);
|
||||
expect(result).toEqual(payload);
|
||||
|
||||
delete globalAny.btoa;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { ITransport, TransportType } from "../src/Transports"
|
||||
import { ITransport, TransportType } from "../src/Transports";
|
||||
|
||||
export function eachTransport(action: (transport: TransportType) => void) {
|
||||
let transportTypes = [
|
||||
const transportTypes = [
|
||||
TransportType.WebSockets,
|
||||
TransportType.ServerSentEvents,
|
||||
TransportType.LongPolling ];
|
||||
transportTypes.forEach(t => action(t));
|
||||
};
|
||||
transportTypes.forEach((t) => action(t));
|
||||
}
|
||||
|
||||
export function eachEndpointUrl(action: (givenUrl: string, expectedUrl: string) => void) {
|
||||
let urls = [
|
||||
const urls = [
|
||||
[ "http://tempuri.org/endpoint/?q=my/Data", "http://tempuri.org/endpoint/negotiate?q=my/Data" ],
|
||||
[ "http://tempuri.org/endpoint?q=my/Data", "http://tempuri.org/endpoint/negotiate?q=my/Data" ],
|
||||
[ "http://tempuri.org/endpoint", "http://tempuri.org/endpoint/negotiate" ],
|
||||
[ "http://tempuri.org/endpoint/", "http://tempuri.org/endpoint/negotiate" ]
|
||||
[ "http://tempuri.org/endpoint/", "http://tempuri.org/endpoint/negotiate" ],
|
||||
];
|
||||
|
||||
urls.forEach(t => action(t[0], t[1]));
|
||||
}
|
||||
urls.forEach((t) => action(t[0], t[1]));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { asyncit as it } from "./Utils"
|
||||
import { TestHttpClient } from "./TestHttpClient";
|
||||
import { HttpRequest } from "../src/index";
|
||||
import { TestHttpClient } from "./TestHttpClient";
|
||||
import { asyncit as it } from "./Utils";
|
||||
|
||||
describe("HttpClient", () => {
|
||||
describe("get", () => {
|
||||
it("sets the method and URL appropriately", async () => {
|
||||
let request: HttpRequest;
|
||||
let testClient = new TestHttpClient().on(r => {
|
||||
const testClient = new TestHttpClient().on((r) => {
|
||||
request = r; return "";
|
||||
});
|
||||
|
||||
|
|
@ -20,21 +20,21 @@ describe("HttpClient", () => {
|
|||
|
||||
it("overrides method and url in options", async () => {
|
||||
let request: HttpRequest;
|
||||
let testClient = new TestHttpClient().on(r => {
|
||||
const testClient = new TestHttpClient().on((r) => {
|
||||
request = r; return "";
|
||||
});
|
||||
|
||||
await testClient.get("http://localhost", {
|
||||
method: "OPTIONS",
|
||||
url: "http://wrong"
|
||||
url: "http://wrong",
|
||||
});
|
||||
expect(request.method).toEqual("GET");
|
||||
expect(request.url).toEqual("http://localhost");
|
||||
})
|
||||
});
|
||||
|
||||
it("copies other options", async () => {
|
||||
let request: HttpRequest;
|
||||
let testClient = new TestHttpClient().on(r => {
|
||||
const testClient = new TestHttpClient().on((r) => {
|
||||
request = r; return "";
|
||||
});
|
||||
|
||||
|
|
@ -42,13 +42,13 @@ describe("HttpClient", () => {
|
|||
timeout: 42,
|
||||
});
|
||||
expect(request.timeout).toEqual(42);
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
describe("post", () => {
|
||||
it("sets the method and URL appropriately", async () => {
|
||||
let request: HttpRequest;
|
||||
let testClient = new TestHttpClient().on(r => {
|
||||
const testClient = new TestHttpClient().on((r) => {
|
||||
request = r; return "";
|
||||
});
|
||||
|
||||
|
|
@ -59,21 +59,21 @@ describe("HttpClient", () => {
|
|||
|
||||
it("overrides method and url in options", async () => {
|
||||
let request: HttpRequest;
|
||||
let testClient = new TestHttpClient().on(r => {
|
||||
const testClient = new TestHttpClient().on((r) => {
|
||||
request = r; return "";
|
||||
});
|
||||
|
||||
await testClient.post("http://localhost", {
|
||||
method: "OPTIONS",
|
||||
url: "http://wrong"
|
||||
url: "http://wrong",
|
||||
});
|
||||
expect(request.method).toEqual("POST");
|
||||
expect(request.url).toEqual("http://localhost");
|
||||
})
|
||||
});
|
||||
|
||||
it("copies other options", async () => {
|
||||
let request: HttpRequest;
|
||||
let testClient = new TestHttpClient().on(r => {
|
||||
const testClient = new TestHttpClient().on((r) => {
|
||||
request = r; return "";
|
||||
});
|
||||
|
||||
|
|
@ -81,6 +81,6 @@ describe("HttpClient", () => {
|
|||
timeout: 42,
|
||||
});
|
||||
expect(request.timeout).toEqual(42);
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { TestHttpClient } from "./TestHttpClient"
|
||||
import { HttpConnection } from "../src/HttpConnection"
|
||||
import { IHttpConnectionOptions } from "../src/HttpConnection"
|
||||
import { DataReceived, TransportClosed } from "../src/Common"
|
||||
import { ITransport, TransportType, TransferMode } from "../src/Transports"
|
||||
import { eachTransport, eachEndpointUrl } from "./Common";
|
||||
import { DataReceived, TransportClosed } from "../src/Common";
|
||||
import { HttpConnection } from "../src/HttpConnection";
|
||||
import { IHttpConnectionOptions } from "../src/HttpConnection";
|
||||
import { HttpResponse } from "../src/index";
|
||||
import { ITransport, TransferMode, TransportType } from "../src/Transports";
|
||||
import { eachEndpointUrl, eachTransport } from "./Common";
|
||||
import { TestHttpClient } from "./TestHttpClient";
|
||||
|
||||
const commonOptions: IHttpConnectionOptions = {
|
||||
logger: null
|
||||
logger: null,
|
||||
};
|
||||
|
||||
describe("HttpConnection", () => {
|
||||
|
|
@ -20,38 +20,37 @@ describe("HttpConnection", () => {
|
|||
});
|
||||
|
||||
it("cannot be created with relative url if window object is not present", () => {
|
||||
(<any>global).window = {};
|
||||
(global as any).window = {};
|
||||
expect(() => new HttpConnection("/test", commonOptions))
|
||||
.toThrow(new Error("Cannot resolve '/test'."));
|
||||
delete (<any>global).window;
|
||||
delete (global as any).window;
|
||||
});
|
||||
|
||||
it("starting connection fails if getting id fails", async (done) => {
|
||||
let options: IHttpConnectionOptions = {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => Promise.reject("error"))
|
||||
.on("GET", r => ""),
|
||||
.on("POST", (r) => Promise.reject("error"))
|
||||
.on("GET", (r) => ""),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
let connection = new HttpConnection("http://tempuri.org", options);
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
try {
|
||||
await connection.start();
|
||||
fail();
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
expect(e).toBe("error");
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it("cannot start a running connection", async (done) => {
|
||||
let options: IHttpConnectionOptions = {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => {
|
||||
.on("POST", (r) => {
|
||||
connection.start()
|
||||
.then(() => {
|
||||
fail();
|
||||
|
|
@ -65,12 +64,11 @@ describe("HttpConnection", () => {
|
|||
}),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
let connection = new HttpConnection("http://tempuri.org", options);
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
try {
|
||||
await connection.start();
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
// This exception is thrown after the actual verification is completed.
|
||||
// The connection is not setup to be running so just ignore the error.
|
||||
}
|
||||
|
|
@ -78,17 +76,17 @@ describe("HttpConnection", () => {
|
|||
|
||||
it("can start a stopped connection", async (done) => {
|
||||
let negotiateCalls = 0;
|
||||
let options: IHttpConnectionOptions = {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => {
|
||||
.on("POST", (r) => {
|
||||
negotiateCalls += 1;
|
||||
return Promise.reject("reached negotiate");
|
||||
})
|
||||
.on("GET", r => ""),
|
||||
.on("GET", (r) => ""),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
let connection = new HttpConnection("http://tempuri.org", options);
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
try {
|
||||
await connection.start();
|
||||
|
|
@ -106,40 +104,39 @@ describe("HttpConnection", () => {
|
|||
});
|
||||
|
||||
it("can stop a starting connection", async (done) => {
|
||||
let options: IHttpConnectionOptions = {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => {
|
||||
.on("POST", (r) => {
|
||||
connection.stop();
|
||||
return "{}";
|
||||
})
|
||||
.on("GET", r => {
|
||||
.on("GET", (r) => {
|
||||
connection.stop();
|
||||
return "";
|
||||
}),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
let connection = new HttpConnection("http://tempuri.org", options);
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
try {
|
||||
await connection.start();
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
fail();
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it("can stop a non-started connection", async (done) => {
|
||||
let connection = new HttpConnection("http://tempuri.org", commonOptions);
|
||||
const connection = new HttpConnection("http://tempuri.org", commonOptions);
|
||||
await connection.stop();
|
||||
done();
|
||||
});
|
||||
|
||||
it("preserves users connection string", async done => {
|
||||
it("preserves users connection string", async (done) => {
|
||||
let connectUrl: string;
|
||||
let fakeTransport: ITransport = {
|
||||
const fakeTransport: ITransport = {
|
||||
connect(url: string): Promise<TransferMode> {
|
||||
connectUrl = url;
|
||||
return Promise.reject(TransferMode.Text);
|
||||
|
|
@ -150,27 +147,25 @@ describe("HttpConnection", () => {
|
|||
stop(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
},
|
||||
onreceive: undefined,
|
||||
onclose: undefined,
|
||||
}
|
||||
onreceive: undefined,
|
||||
};
|
||||
|
||||
let options: IHttpConnectionOptions = {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => "{ \"connectionId\": \"42\" }")
|
||||
.on("GET", r => ""),
|
||||
.on("POST", (r) => "{ \"connectionId\": \"42\" }")
|
||||
.on("GET", (r) => ""),
|
||||
transport: fakeTransport,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
||||
let connection = new HttpConnection("http://tempuri.org?q=myData", options);
|
||||
const connection = new HttpConnection("http://tempuri.org?q=myData", options);
|
||||
|
||||
try {
|
||||
await connection.start();
|
||||
fail();
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
expect(connectUrl).toBe("http://tempuri.org?q=myData&id=42");
|
||||
|
|
@ -178,18 +173,18 @@ describe("HttpConnection", () => {
|
|||
});
|
||||
|
||||
eachEndpointUrl((givenUrl: string, expectedUrl: string) => {
|
||||
it("negotiate request puts 'negotiate' at the end of the path", async done => {
|
||||
it("negotiate request puts 'negotiate' at the end of the path", async (done) => {
|
||||
let negotiateUrl: string;
|
||||
let connection: HttpConnection;
|
||||
let options: IHttpConnectionOptions = {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => {
|
||||
.on("POST", (r) => {
|
||||
negotiateUrl = r.url;
|
||||
connection.stop();
|
||||
return "{}";
|
||||
})
|
||||
.on("GET", r => {
|
||||
.on("GET", (r) => {
|
||||
connection.stop();
|
||||
return "";
|
||||
}),
|
||||
|
|
@ -214,62 +209,59 @@ describe("HttpConnection", () => {
|
|||
if (requestedTransport === TransportType.WebSockets) {
|
||||
return;
|
||||
}
|
||||
it(`cannot be started if requested ${TransportType[requestedTransport]} transport not available on server`, async done => {
|
||||
let options: IHttpConnectionOptions = {
|
||||
it(`cannot be started if requested ${TransportType[requestedTransport]} transport not available on server`, async (done) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||
.on("GET", r => ""),
|
||||
.on("POST", (r) => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||
.on("GET", (r) => ""),
|
||||
transport: requestedTransport,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
let connection = new HttpConnection("http://tempuri.org", options);
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
try {
|
||||
await connection.start();
|
||||
fail();
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
expect(e.message).toBe("No available transports found.");
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("cannot be started if no transport available on server and no transport requested", async done => {
|
||||
let options: IHttpConnectionOptions = {
|
||||
it("cannot be started if no transport available on server and no transport requested", async (done) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||
.on("GET", r => ""),
|
||||
.on("POST", (r) => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||
.on("GET", (r) => ""),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
let connection = new HttpConnection("http://tempuri.org", options);
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
try {
|
||||
await connection.start();
|
||||
fail();
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
expect(e.message).toBe("No available transports found.");
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not send negotiate request if WebSockets transport requested explicitly', async done => {
|
||||
let options: IHttpConnectionOptions = {
|
||||
it("does not send negotiate request if WebSockets transport requested explicitly", async (done) => {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient(),
|
||||
transport: TransportType.WebSockets,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
let connection = new HttpConnection("http://tempuri.org", options);
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
try {
|
||||
await connection.start();
|
||||
fail();
|
||||
done();
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
// WebSocket is created when the transport is connecting which happens after
|
||||
// negotiate request would be sent. No better/easier way to test this.
|
||||
expect(e.message).toBe("WebSocket is not defined");
|
||||
|
|
@ -284,28 +276,28 @@ describe("HttpConnection", () => {
|
|||
[TransferMode.Binary, TransferMode.Binary],
|
||||
].forEach(([requestedTransferMode, transportTransferMode]) => {
|
||||
it(`connection returns ${transportTransferMode} transfer mode when ${requestedTransferMode} transfer mode is requested`, async () => {
|
||||
let fakeTransport = {
|
||||
const fakeTransport = {
|
||||
// mode: TransferMode : TransferMode.Text
|
||||
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> { return Promise.resolve(transportTransferMode); },
|
||||
mode: transportTransferMode,
|
||||
onclose: null,
|
||||
onreceive: null,
|
||||
send(data: any): Promise<void> { return Promise.resolve(); },
|
||||
stop(): Promise<void> { return Promise.resolve(); },
|
||||
onreceive: null,
|
||||
onclose: null,
|
||||
mode: transportTransferMode
|
||||
} as ITransport;
|
||||
|
||||
let options: IHttpConnectionOptions = {
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||
.on("GET", r => ""),
|
||||
.on("POST", (r) => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||
.on("GET", (r) => ""),
|
||||
transport: fakeTransport,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
let connection = new HttpConnection("https://tempuri.org", options);
|
||||
const connection = new HttpConnection("https://tempuri.org", options);
|
||||
connection.features.transferMode = requestedTransferMode;
|
||||
await connection.start();
|
||||
let actualTransferMode = connection.features.transferMode;
|
||||
const actualTransferMode = connection.features.transferMode;
|
||||
|
||||
expect(actualTransferMode).toBe(transportTransferMode);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,32 +1,32 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { IConnection } from "../src/IConnection"
|
||||
import { HubConnection } from "../src/HubConnection"
|
||||
import { DataReceived, ConnectionClosed } from "../src/Common"
|
||||
import { TransportType, ITransport, TransferMode } from "../src/Transports"
|
||||
import { Observer } from "../src/Observable"
|
||||
import { TextMessageFormat } from "../src/TextMessageFormat"
|
||||
import { ILogger, LogLevel } from "../src/ILogger"
|
||||
import { MessageType } from "../src/IHubProtocol"
|
||||
import { ConnectionClosed, DataReceived } from "../src/Common";
|
||||
import { HubConnection } from "../src/HubConnection";
|
||||
import { IConnection } from "../src/IConnection";
|
||||
import { MessageType } from "../src/IHubProtocol";
|
||||
import { ILogger, LogLevel } from "../src/ILogger";
|
||||
import { Observer } from "../src/Observable";
|
||||
import { TextMessageFormat } from "../src/TextMessageFormat";
|
||||
import { ITransport, TransferMode, TransportType } from "../src/Transports";
|
||||
|
||||
import { asyncit as it, captureException, delay, PromiseSource } from './Utils';
|
||||
import { IHubConnectionOptions } from "../src/HubConnection";
|
||||
import { asyncit as it, captureException, delay, PromiseSource } from "./Utils";
|
||||
|
||||
const commonOptions: IHubConnectionOptions = {
|
||||
logger: null,
|
||||
}
|
||||
};
|
||||
|
||||
describe("HubConnection", () => {
|
||||
|
||||
describe("start", () => {
|
||||
it("sends negotiation message", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
await hubConnection.start();
|
||||
expect(connection.sentData.length).toBe(1)
|
||||
expect(connection.sentData.length).toBe(1);
|
||||
expect(JSON.parse(connection.sentData[0])).toEqual({
|
||||
protocol: "json"
|
||||
protocol: "json",
|
||||
});
|
||||
await hubConnection.stop();
|
||||
});
|
||||
|
|
@ -34,21 +34,21 @@ describe("HubConnection", () => {
|
|||
|
||||
describe("send", () => {
|
||||
it("sends a non blocking invocation", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invokePromise = hubConnection.send("testMethod", "arg", 42)
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const invokePromise = hubConnection.send("testMethod", "arg", 42)
|
||||
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
|
||||
|
||||
// Verify the message is sent
|
||||
expect(connection.sentData.length).toBe(1);
|
||||
expect(JSON.parse(connection.sentData[0])).toEqual({
|
||||
type: MessageType.Invocation,
|
||||
target: "testMethod",
|
||||
arguments: [
|
||||
"arg",
|
||||
42
|
||||
]
|
||||
42,
|
||||
],
|
||||
target: "testMethod",
|
||||
type: MessageType.Invocation,
|
||||
});
|
||||
|
||||
// Close the connection
|
||||
|
|
@ -58,22 +58,22 @@ describe("HubConnection", () => {
|
|||
|
||||
describe("invoke", () => {
|
||||
it("sends an invocation", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invokePromise = hubConnection.invoke("testMethod", "arg", 42)
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const invokePromise = hubConnection.invoke("testMethod", "arg", 42)
|
||||
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
|
||||
|
||||
// Verify the message is sent
|
||||
expect(connection.sentData.length).toBe(1);
|
||||
expect(JSON.parse(connection.sentData[0])).toEqual({
|
||||
type: MessageType.Invocation,
|
||||
invocationId: connection.lastInvocationId,
|
||||
target: "testMethod",
|
||||
arguments: [
|
||||
"arg",
|
||||
42
|
||||
]
|
||||
42,
|
||||
],
|
||||
invocationId: connection.lastInvocationId,
|
||||
target: "testMethod",
|
||||
type: MessageType.Invocation,
|
||||
});
|
||||
|
||||
// Close the connection
|
||||
|
|
@ -81,22 +81,22 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("rejects the promise when an error is received", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||
|
||||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, error: "foo" });
|
||||
|
||||
let ex = await captureException(async () => invokePromise);
|
||||
const ex = await captureException(async () => invokePromise);
|
||||
expect(ex.message).toBe("foo");
|
||||
});
|
||||
|
||||
it("resolves the promise when a result is received", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||
|
||||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, result: "foo" });
|
||||
|
||||
|
|
@ -104,84 +104,84 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("completes pending invocations when stopped", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invokePromise = hubConnection.invoke("testMethod");
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const invokePromise = hubConnection.invoke("testMethod");
|
||||
hubConnection.stop();
|
||||
|
||||
let ex = await captureException(async () => await invokePromise);
|
||||
const ex = await captureException(async () => await invokePromise);
|
||||
expect(ex.message).toBe("Invocation canceled due to connection being closed.");
|
||||
});
|
||||
|
||||
it("completes pending invocations when connection is lost", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invokePromise = hubConnection.invoke("testMethod");
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const invokePromise = hubConnection.invoke("testMethod");
|
||||
// Typically this would be called by the transport
|
||||
connection.onclose(new Error("Connection lost"));
|
||||
|
||||
let ex = await captureException(async () => await invokePromise);
|
||||
const ex = await captureException(async () => await invokePromise);
|
||||
expect(ex.message).toBe("Connection lost");
|
||||
});
|
||||
});
|
||||
|
||||
describe("on", () => {
|
||||
it("invocations ignored in callbacks not registered", async () => {
|
||||
let warnings: string[] = [];
|
||||
let logger = <ILogger>{
|
||||
log: function (logLevel: LogLevel, message: string) {
|
||||
const warnings: string[] = [];
|
||||
const logger = {
|
||||
log: (logLevel: LogLevel, message: string) => {
|
||||
if (logLevel === LogLevel.Warning) {
|
||||
warnings.push(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, { logger: logger });
|
||||
},
|
||||
} as ILogger;
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, { logger });
|
||||
|
||||
connection.receive({
|
||||
type: MessageType.Invocation,
|
||||
invocationId: 0,
|
||||
target: "message",
|
||||
arguments: ["test"],
|
||||
nonblocking: true
|
||||
invocationId: 0,
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
});
|
||||
|
||||
expect(warnings).toEqual(["No client method with the name 'message' found."]);
|
||||
});
|
||||
|
||||
it("callback invoked when servers invokes a method on the client", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
let value = "";
|
||||
hubConnection.on("message", v => value = v);
|
||||
hubConnection.on("message", (v) => value = v);
|
||||
|
||||
connection.receive({
|
||||
type: MessageType.Invocation,
|
||||
invocationId: 0,
|
||||
target: "message",
|
||||
arguments: ["test"],
|
||||
nonblocking: true
|
||||
invocationId: 0,
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
});
|
||||
|
||||
expect(value).toBe("test");
|
||||
});
|
||||
|
||||
it("can have multiple callbacks", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
let numInvocations1 = 0;
|
||||
let numInvocations2 = 0;
|
||||
hubConnection.on("message", () => numInvocations1++);
|
||||
hubConnection.on("message", () => numInvocations2++);
|
||||
|
||||
connection.receive({
|
||||
type: MessageType.Invocation,
|
||||
invocationId: 0,
|
||||
target: "message",
|
||||
arguments: [],
|
||||
nonblocking: true
|
||||
invocationId: 0,
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
});
|
||||
|
||||
expect(numInvocations1).toBe(1);
|
||||
|
|
@ -189,56 +189,56 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("can unsubscribe from on", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
|
||||
var numInvocations = 0;
|
||||
var callback = () => numInvocations++;
|
||||
let numInvocations = 0;
|
||||
const callback = () => numInvocations++;
|
||||
hubConnection.on("message", callback);
|
||||
|
||||
connection.receive({
|
||||
type: MessageType.Invocation,
|
||||
invocationId: 0,
|
||||
target: "message",
|
||||
arguments: [],
|
||||
nonblocking: true
|
||||
invocationId: 0,
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
});
|
||||
|
||||
hubConnection.off("message", callback);
|
||||
|
||||
connection.receive({
|
||||
type: MessageType.Invocation,
|
||||
invocationId: 0,
|
||||
target: "message",
|
||||
arguments: [],
|
||||
nonblocking: true
|
||||
invocationId: 0,
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
});
|
||||
|
||||
expect(numInvocations).toBe(1);
|
||||
});
|
||||
|
||||
it("unsubscribing from non-existing callbacks no-ops", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
|
||||
hubConnection.off("_", () => { });
|
||||
hubConnection.on("message", t => { });
|
||||
hubConnection.on("message", (t) => { });
|
||||
hubConnection.on("message", () => { });
|
||||
});
|
||||
|
||||
it("using null/undefined for methodName or method no-ops", async () => {
|
||||
let warnings: string[] = [];
|
||||
let logger = <ILogger>{
|
||||
log: function (logLevel: LogLevel, message: string) {
|
||||
const warnings: string[] = [];
|
||||
const logger = {
|
||||
log(logLevel: LogLevel, message: string) {
|
||||
if (logLevel === LogLevel.Warning) {
|
||||
warnings.push(message);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
},
|
||||
} as ILogger;
|
||||
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, { logger: logger });
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, { logger });
|
||||
|
||||
hubConnection.on(null, undefined);
|
||||
hubConnection.on(undefined, null);
|
||||
|
|
@ -249,11 +249,11 @@ describe("HubConnection", () => {
|
|||
|
||||
// invoke a method to make sure we are not trying to use null/undefined
|
||||
connection.receive({
|
||||
type: MessageType.Invocation,
|
||||
invocationId: 0,
|
||||
target: "message",
|
||||
arguments: [],
|
||||
nonblocking: true
|
||||
invocationId: 0,
|
||||
nonblocking: true,
|
||||
target: "message",
|
||||
type: MessageType.Invocation,
|
||||
});
|
||||
|
||||
expect(warnings).toEqual(["No client method with the name 'message' found."]);
|
||||
|
|
@ -269,21 +269,21 @@ describe("HubConnection", () => {
|
|||
|
||||
describe("stream", () => {
|
||||
it("sends an invocation", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invokePromise = hubConnection.stream("testStream", "arg", 42);
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const invokePromise = hubConnection.stream("testStream", "arg", 42);
|
||||
|
||||
// Verify the message is sent
|
||||
expect(connection.sentData.length).toBe(1);
|
||||
expect(JSON.parse(connection.sentData[0])).toEqual({
|
||||
type: MessageType.StreamInvocation,
|
||||
invocationId: connection.lastInvocationId,
|
||||
target: "testStream",
|
||||
arguments: [
|
||||
"arg",
|
||||
42
|
||||
]
|
||||
42,
|
||||
],
|
||||
invocationId: connection.lastInvocationId,
|
||||
target: "testStream",
|
||||
type: MessageType.StreamInvocation,
|
||||
});
|
||||
|
||||
// Close the connection
|
||||
|
|
@ -291,24 +291,24 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("completes with an error when an error is yielded", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let observer = new TestObserver();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const observer = new TestObserver();
|
||||
hubConnection.stream<any>("testMethod", "arg", 42)
|
||||
.subscribe(observer);
|
||||
|
||||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, error: "foo" });
|
||||
|
||||
let ex = await captureException(async () => await observer.completed);
|
||||
const ex = await captureException(async () => await observer.completed);
|
||||
expect(ex.message).toEqual("Error: foo");
|
||||
});
|
||||
|
||||
it("completes the observer when a completion is received", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let observer = new TestObserver();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const observer = new TestObserver();
|
||||
hubConnection.stream<any>("testMethod", "arg", 42)
|
||||
.subscribe(observer);
|
||||
|
||||
|
|
@ -318,38 +318,38 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("completes pending streams when stopped", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let observer = new TestObserver();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const observer = new TestObserver();
|
||||
hubConnection.stream<any>("testMethod")
|
||||
.subscribe(observer);
|
||||
hubConnection.stop();
|
||||
|
||||
let ex = await captureException(async () => await observer.completed);
|
||||
const ex = await captureException(async () => await observer.completed);
|
||||
expect(ex.message).toEqual("Error: Invocation canceled due to connection being closed.");
|
||||
});
|
||||
|
||||
it("completes pending streams when connection is lost", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let observer = new TestObserver();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const observer = new TestObserver();
|
||||
hubConnection.stream<any>("testMethod")
|
||||
.subscribe(observer);
|
||||
|
||||
// Typically this would be called by the transport
|
||||
connection.onclose(new Error("Connection lost"));
|
||||
|
||||
let ex = await captureException(async () => await observer.completed);
|
||||
const ex = await captureException(async () => await observer.completed);
|
||||
expect(ex.message).toEqual("Error: Connection lost");
|
||||
});
|
||||
|
||||
it("yields items as they arrive", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let observer = new TestObserver();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const observer = new TestObserver();
|
||||
hubConnection.stream<any>("testMethod")
|
||||
.subscribe(observer);
|
||||
|
||||
|
|
@ -367,11 +367,11 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("does not require error function registered", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let observer = hubConnection.stream("testMethod").subscribe({
|
||||
next: val => { }
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const observer = hubConnection.stream("testMethod").subscribe({
|
||||
next: (val) => { },
|
||||
});
|
||||
|
||||
// Typically this would be called by the transport
|
||||
|
|
@ -380,11 +380,11 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("does not require complete function registered", async () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let observer = hubConnection.stream("testMethod").subscribe({
|
||||
next: val => { }
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const observer = hubConnection.stream("testMethod").subscribe({
|
||||
next: (val) => { },
|
||||
});
|
||||
|
||||
// Send completion to trigger observer.complete()
|
||||
|
|
@ -393,11 +393,11 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("can be canceled", () => {
|
||||
let connection = new TestConnection();
|
||||
const connection = new TestConnection();
|
||||
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let observer = new TestObserver();
|
||||
let subscription = hubConnection.stream("testMethod")
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const observer = new TestObserver();
|
||||
const subscription = hubConnection.stream("testMethod")
|
||||
.subscribe(observer);
|
||||
|
||||
connection.receive({ type: MessageType.StreamItem, invocationId: connection.lastInvocationId, item: 1 });
|
||||
|
|
@ -412,29 +412,29 @@ describe("HubConnection", () => {
|
|||
// Verify the cancel is sent
|
||||
expect(connection.sentData.length).toBe(2);
|
||||
expect(JSON.parse(connection.sentData[1])).toEqual({
|
||||
invocationId: connection.lastInvocationId,
|
||||
type: MessageType.CancelInvocation,
|
||||
invocationId: connection.lastInvocationId
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("onClose", () => {
|
||||
it("can have multiple callbacks", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invocations = 0;
|
||||
hubConnection.onclose(e => invocations++);
|
||||
hubConnection.onclose(e => invocations++);
|
||||
hubConnection.onclose((e) => invocations++);
|
||||
hubConnection.onclose((e) => invocations++);
|
||||
// Typically this would be called by the transport
|
||||
connection.onclose();
|
||||
expect(invocations).toBe(2);
|
||||
});
|
||||
|
||||
it("callbacks receive error", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
let error: Error;
|
||||
hubConnection.onclose(e => error = e);
|
||||
hubConnection.onclose((e) => error = e);
|
||||
|
||||
// Typically this would be called by the transport
|
||||
connection.onclose(new Error("Test error."));
|
||||
|
|
@ -442,8 +442,8 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("ignores null callbacks", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
hubConnection.onclose(null);
|
||||
hubConnection.onclose(undefined);
|
||||
// Typically this would be called by the transport
|
||||
|
|
@ -456,9 +456,9 @@ describe("HubConnection", () => {
|
|||
it("can receive ping messages", async () => {
|
||||
// Receive the ping mid-invocation so we can see that the rest of the flow works fine
|
||||
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, commonOptions);
|
||||
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, commonOptions);
|
||||
const invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||
|
||||
connection.receive({ type: MessageType.Ping });
|
||||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, result: "foo" });
|
||||
|
|
@ -467,11 +467,11 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
it("does not terminate if messages are received", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 });
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 });
|
||||
|
||||
let p = new PromiseSource<Error>();
|
||||
hubConnection.onclose(error => p.resolve(error));
|
||||
const p = new PromiseSource<Error>();
|
||||
hubConnection.onclose((e) => p.resolve(e));
|
||||
|
||||
await hubConnection.start();
|
||||
|
||||
|
|
@ -486,69 +486,67 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.stop();
|
||||
|
||||
let error = await p.promise;
|
||||
const error = await p.promise;
|
||||
|
||||
expect(error).toBeUndefined();
|
||||
});
|
||||
|
||||
it("terminates if no messages received within timeout interval", async () => {
|
||||
let connection = new TestConnection();
|
||||
let hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 });
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 });
|
||||
|
||||
let p = new PromiseSource<Error>();
|
||||
hubConnection.onclose(error => p.resolve(error));
|
||||
const p = new PromiseSource<Error>();
|
||||
hubConnection.onclose((e) => p.resolve(e));
|
||||
|
||||
await hubConnection.start();
|
||||
|
||||
let error = await p.promise;
|
||||
const error = await p.promise;
|
||||
|
||||
expect(error).toEqual(new Error("Server timeout elapsed without receiving a message from the server."));
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
class TestConnection implements IConnection {
|
||||
readonly features: any = {};
|
||||
public readonly features: any = {};
|
||||
|
||||
start(): Promise<void> {
|
||||
public start(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
send(data: any): Promise<void> {
|
||||
let invocation = TextMessageFormat.parse(data)[0];
|
||||
let invocationId = JSON.parse(invocation).invocationId;
|
||||
public send(data: any): Promise<void> {
|
||||
const invocation = TextMessageFormat.parse(data)[0];
|
||||
const invocationId = JSON.parse(invocation).invocationId;
|
||||
if (invocationId) {
|
||||
this.lastInvocationId = invocationId;
|
||||
}
|
||||
if (this.sentData) {
|
||||
this.sentData.push(invocation);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.sentData = [invocation];
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
stop(error?: Error): Promise<void> {
|
||||
public stop(error?: Error): Promise<void> {
|
||||
if (this.onclose) {
|
||||
this.onclose(error);
|
||||
}
|
||||
return Promise.resolve();
|
||||
};
|
||||
}
|
||||
|
||||
receive(data: any): void {
|
||||
let payload = JSON.stringify(data);
|
||||
public receive(data: any): void {
|
||||
const payload = JSON.stringify(data);
|
||||
this.onreceive(TextMessageFormat.write(payload));
|
||||
}
|
||||
|
||||
onreceive: DataReceived;
|
||||
onclose: ConnectionClosed;
|
||||
sentData: any[];
|
||||
lastInvocationId: string;
|
||||
};
|
||||
public onreceive: DataReceived;
|
||||
public onclose: ConnectionClosed;
|
||||
public sentData: any[];
|
||||
public lastInvocationId: string;
|
||||
}
|
||||
|
||||
class TestObserver implements Observer<any>
|
||||
{
|
||||
class TestObserver implements Observer<any> {
|
||||
public itemsReceived: [any];
|
||||
private itemsSource: PromiseSource<[any]>;
|
||||
|
||||
|
|
@ -557,19 +555,19 @@ class TestObserver implements Observer<any>
|
|||
}
|
||||
|
||||
constructor() {
|
||||
this.itemsReceived = <[any]>[];
|
||||
this.itemsReceived = [] as [any];
|
||||
this.itemsSource = new PromiseSource<[any]>();
|
||||
}
|
||||
|
||||
next(value: any) {
|
||||
public next(value: any) {
|
||||
this.itemsReceived.push(value);
|
||||
}
|
||||
|
||||
error(err: any) {
|
||||
public error(err: any) {
|
||||
this.itemsSource.reject(new Error(err));
|
||||
}
|
||||
|
||||
complete() {
|
||||
public complete() {
|
||||
this.itemsSource.resolve(this.itemsReceived);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { LoggerFactory } from "../src/Loggers"
|
||||
import { ILogger, LogLevel } from "../src/ILogger"
|
||||
import { ILogger, LogLevel } from "../src/ILogger";
|
||||
import { LoggerFactory } from "../src/Loggers";
|
||||
|
||||
describe("LoggerFactory", () => {
|
||||
it("creates ConsoleLogger when no logging specified", () => {
|
||||
|
|
@ -18,7 +18,7 @@ describe("LoggerFactory", () => {
|
|||
});
|
||||
|
||||
it("does not create its own logger if the user provides one", () => {
|
||||
let customLogger : ILogger = { log: LogLevel => {} };
|
||||
const customLogger: ILogger = { log: (logLevel) => {} };
|
||||
expect(LoggerFactory.createLogger(customLogger)).toBe(customLogger);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { HttpClient, HttpRequest, HttpResponse } from "../src/HttpClient"
|
||||
import { HttpClient, HttpRequest, HttpResponse } from "../src/HttpClient";
|
||||
|
||||
type TestHttpHandlerResult = HttpResponse | string;
|
||||
export type TestHttpHandler = (request: HttpRequest, next?: (request: HttpRequest) => Promise<HttpResponse>) => Promise<TestHttpHandlerResult> | TestHttpHandlerResult;
|
||||
|
|
@ -16,28 +16,26 @@ export class TestHttpClient extends HttpClient {
|
|||
|
||||
}
|
||||
|
||||
send(request: HttpRequest): Promise<HttpResponse> {
|
||||
public send(request: HttpRequest): Promise<HttpResponse> {
|
||||
return this.handler(request);
|
||||
}
|
||||
|
||||
on(handler: TestHttpHandler): TestHttpClient;
|
||||
on(method: string | RegExp, handler: TestHttpHandler): TestHttpClient;
|
||||
on(method: string | RegExp, url: string, handler: TestHttpHandler): TestHttpClient;
|
||||
on(method: string | RegExp, url: RegExp, handler: TestHttpHandler): TestHttpClient;
|
||||
on(methodOrHandler: string | RegExp | TestHttpHandler, urlOrHandler?: string | RegExp | TestHttpHandler, handler?: TestHttpHandler): TestHttpClient {
|
||||
public on(handler: TestHttpHandler): TestHttpClient;
|
||||
public on(method: string | RegExp, handler: TestHttpHandler): TestHttpClient;
|
||||
public on(method: string | RegExp, url: string, handler: TestHttpHandler): TestHttpClient;
|
||||
public on(method: string | RegExp, url: RegExp, handler: TestHttpHandler): TestHttpClient;
|
||||
public on(methodOrHandler: string | RegExp | TestHttpHandler, urlOrHandler?: string | RegExp | TestHttpHandler, handler?: TestHttpHandler): TestHttpClient {
|
||||
let method: string | RegExp;
|
||||
let url: string | RegExp;
|
||||
if ((typeof methodOrHandler === "string") || (methodOrHandler instanceof RegExp)) {
|
||||
method = methodOrHandler;
|
||||
}
|
||||
else if (methodOrHandler) {
|
||||
} else if (methodOrHandler) {
|
||||
handler = methodOrHandler;
|
||||
}
|
||||
|
||||
if ((typeof urlOrHandler === "string") || (urlOrHandler instanceof RegExp)) {
|
||||
url = urlOrHandler;
|
||||
}
|
||||
else if (urlOrHandler) {
|
||||
} else if (urlOrHandler) {
|
||||
handler = urlOrHandler;
|
||||
}
|
||||
|
||||
|
|
@ -46,10 +44,10 @@ export class TestHttpClient extends HttpClient {
|
|||
throw new Error("Missing required argument: 'handler'");
|
||||
}
|
||||
|
||||
let oldHandler = this.handler;
|
||||
let newHandler = async (request: HttpRequest) => {
|
||||
const oldHandler = this.handler;
|
||||
const newHandler = async (request: HttpRequest) => {
|
||||
if (matches(method, request.method) && matches(url, request.url)) {
|
||||
let promise = handler(request, oldHandler);
|
||||
const promise = handler(request, oldHandler);
|
||||
|
||||
let val: TestHttpHandlerResult;
|
||||
if (promise instanceof Promise) {
|
||||
|
|
@ -60,12 +58,10 @@ export class TestHttpClient extends HttpClient {
|
|||
|
||||
if (typeof val === "string") {
|
||||
return new HttpResponse(200, "OK", val);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return await oldHandler(request);
|
||||
}
|
||||
};
|
||||
|
|
@ -83,8 +79,7 @@ function matches(pattern: string | RegExp, actual: string): boolean {
|
|||
|
||||
if (typeof pattern === "string") {
|
||||
return actual === pattern;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return pattern.test(actual);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { TextMessageFormat } from "../src/TextMessageFormat"
|
||||
import { TextMessageFormat } from "../src/TextMessageFormat";
|
||||
|
||||
describe("Text Message Formatter", () => {
|
||||
([
|
||||
|
|
@ -9,20 +9,20 @@ describe("Text Message Formatter", () => {
|
|||
["\u001e\u001e", ["", ""]],
|
||||
["Hello\u001e", ["Hello"]],
|
||||
["Hello,\u001eWorld!\u001e", ["Hello,", "World!"]],
|
||||
] as [string, string[]][]).forEach(([payload, expected_messages]) => {
|
||||
] as Array<[string, string[]]>).forEach(([payload, expectedMessages]) => {
|
||||
it(`should parse '${payload}' correctly`, () => {
|
||||
let messages = TextMessageFormat.parse(payload);
|
||||
expect(messages).toEqual(expected_messages);
|
||||
})
|
||||
const messages = TextMessageFormat.parse(payload);
|
||||
expect(messages).toEqual(expectedMessages);
|
||||
});
|
||||
});
|
||||
|
||||
([
|
||||
["", new Error("Message is incomplete.")],
|
||||
["ABC", new Error("Message is incomplete.")],
|
||||
["ABC\u001eXYZ", new Error("Message is incomplete.")],
|
||||
] as [string, Error][]).forEach(([payload, expected_error]) => {
|
||||
] as Array<[string, Error]>).forEach(([payload, expectedError]) => {
|
||||
it(`should fail to parse '${payload}'`, () => {
|
||||
expect(() => TextMessageFormat.parse(payload)).toThrow(expected_error);
|
||||
expect(() => TextMessageFormat.parse(payload)).toThrow(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@
|
|||
export function asyncit(expectation: string, assertion?: () => Promise<any> | void, timeout?: number): void {
|
||||
let testFunction: (done: DoneFn) => void;
|
||||
if (assertion) {
|
||||
testFunction = done => {
|
||||
let promise = assertion();
|
||||
testFunction = (done) => {
|
||||
const promise = assertion();
|
||||
if (promise) {
|
||||
promise.then(() => done())
|
||||
.catch((err) => {
|
||||
|
|
@ -31,13 +31,13 @@ export async function captureException(fn: () => Promise<any>): Promise<Error> {
|
|||
}
|
||||
|
||||
export function delay(durationInMilliseconds: number): Promise<void> {
|
||||
let source = new PromiseSource<void>();
|
||||
const source = new PromiseSource<void>();
|
||||
setTimeout(() => source.resolve(), durationInMilliseconds);
|
||||
return source.promise;
|
||||
}
|
||||
|
||||
export class PromiseSource<T> {
|
||||
public promise: Promise<T>
|
||||
public promise: Promise<T>;
|
||||
|
||||
private resolver: (value?: T | PromiseLike<T>) => void;
|
||||
private rejecter: (reason?: any) => void;
|
||||
|
|
@ -49,11 +49,11 @@ export class PromiseSource<T> {
|
|||
});
|
||||
}
|
||||
|
||||
resolve(value?: T | PromiseLike<T>) {
|
||||
public resolve(value?: T | PromiseLike<T>) {
|
||||
this.resolver(value);
|
||||
}
|
||||
|
||||
reject(reason?: any) {
|
||||
public reject(reason?: any) {
|
||||
this.rejecter(reason);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export class AbortController implements AbortSignal {
|
|||
private isAborted: boolean = false;
|
||||
public onabort: () => void;
|
||||
|
||||
abort() {
|
||||
public abort() {
|
||||
if (!this.isAborted) {
|
||||
this.isAborted = true;
|
||||
if (this.onabort) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { IHubProtocol, HubMessage, ProtocolType } from "./IHubProtocol"
|
||||
import { HubMessage, IHubProtocol, ProtocolType } from "./IHubProtocol";
|
||||
|
||||
export class Base64EncodedHubProtocol implements IHubProtocol {
|
||||
private wrappedProtocol: IHubProtocol;
|
||||
|
|
@ -12,49 +12,49 @@ export class Base64EncodedHubProtocol implements IHubProtocol {
|
|||
this.type = ProtocolType.Text;
|
||||
}
|
||||
|
||||
readonly name: string;
|
||||
readonly type: ProtocolType;
|
||||
public readonly name: string;
|
||||
public readonly type: ProtocolType;
|
||||
|
||||
parseMessages(input: any): HubMessage[] {
|
||||
public parseMessages(input: any): HubMessage[] {
|
||||
// The format of the message is `size:message;`
|
||||
let pos = input.indexOf(":");
|
||||
if (pos == -1 || input[input.length - 1] != ';') {
|
||||
const pos = input.indexOf(":");
|
||||
if (pos === -1 || input[input.length - 1] !== ";") {
|
||||
throw new Error("Invalid payload.");
|
||||
}
|
||||
|
||||
let lenStr = input.substring(0, pos);
|
||||
const lenStr = input.substring(0, pos);
|
||||
if (!/^[0-9]+$/.test(lenStr)) {
|
||||
throw new Error(`Invalid length: '${lenStr}'`);
|
||||
}
|
||||
|
||||
let messageSize = parseInt(lenStr, 10);
|
||||
const messageSize = parseInt(lenStr, 10);
|
||||
// 2 accounts for ':' after message size and trailing ';'
|
||||
if (messageSize != input.length - pos - 2) {
|
||||
if (messageSize !== input.length - pos - 2) {
|
||||
throw new Error("Invalid message size.");
|
||||
}
|
||||
|
||||
let encodedMessage = input.substring(pos + 1, input.length - 1);
|
||||
const encodedMessage = input.substring(pos + 1, input.length - 1);
|
||||
|
||||
// atob/btoa are browsers APIs but they can be polyfilled. If this becomes problematic we can use
|
||||
// base64-js module
|
||||
let s = atob(encodedMessage);
|
||||
let payload = new Uint8Array(s.length);
|
||||
const s = atob(encodedMessage);
|
||||
const payload = new Uint8Array(s.length);
|
||||
for (let i = 0; i < payload.length; i++) {
|
||||
payload[i] = s.charCodeAt(i);
|
||||
}
|
||||
return this.wrappedProtocol.parseMessages(payload.buffer);
|
||||
}
|
||||
|
||||
writeMessage(message: HubMessage): any {
|
||||
let payload = new Uint8Array(this.wrappedProtocol.writeMessage(message));
|
||||
public writeMessage(message: HubMessage): any {
|
||||
const payload = new Uint8Array(this.wrappedProtocol.writeMessage(message));
|
||||
let s = "";
|
||||
for (let i = 0; i < payload.byteLength; i++) {
|
||||
s += String.fromCharCode(payload[i]);
|
||||
}
|
||||
// atob/btoa are browsers APIs but they can be polyfilled. If this becomes problematic we can use
|
||||
// base64-js module
|
||||
let encodedMessage = btoa(s);
|
||||
const encodedMessage = btoa(s);
|
||||
|
||||
return `${encodedMessage.length.toString()}:${encodedMessage};`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
export class HttpError extends Error {
|
||||
statusCode: number;
|
||||
public statusCode: number;
|
||||
constructor(errorMessage: string, statusCode: number) {
|
||||
super(errorMessage);
|
||||
this.statusCode = statusCode;
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { TimeoutError, HttpError } from "./Errors";
|
||||
import { AbortSignal } from "./AbortController";
|
||||
import { HttpError, TimeoutError } from "./Errors";
|
||||
|
||||
export interface HttpRequest {
|
||||
method?: string,
|
||||
url?: string,
|
||||
content?: string | ArrayBuffer,
|
||||
headers?: Map<string, string>,
|
||||
responseType?: XMLHttpRequestResponseType,
|
||||
abortSignal?: AbortSignal,
|
||||
timeout?: number,
|
||||
method?: string;
|
||||
url?: string;
|
||||
content?: string | ArrayBuffer;
|
||||
headers?: Map<string, string>;
|
||||
responseType?: XMLHttpRequestResponseType;
|
||||
abortSignal?: AbortSignal;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export class HttpResponse {
|
||||
|
|
@ -25,33 +25,33 @@ export class HttpResponse {
|
|||
}
|
||||
|
||||
export abstract class HttpClient {
|
||||
get(url: string): Promise<HttpResponse>;
|
||||
get(url: string, options: HttpRequest): Promise<HttpResponse>;
|
||||
get(url: string, options?: HttpRequest): Promise<HttpResponse> {
|
||||
public get(url: string): Promise<HttpResponse>;
|
||||
public get(url: string, options: HttpRequest): Promise<HttpResponse>;
|
||||
public get(url: string, options?: HttpRequest): Promise<HttpResponse> {
|
||||
return this.send({
|
||||
...options,
|
||||
method: "GET",
|
||||
url: url,
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
post(url: string): Promise<HttpResponse>;
|
||||
post(url: string, options: HttpRequest): Promise<HttpResponse>;
|
||||
post(url: string, options?: HttpRequest): Promise<HttpResponse> {
|
||||
public post(url: string): Promise<HttpResponse>;
|
||||
public post(url: string, options: HttpRequest): Promise<HttpResponse>;
|
||||
public post(url: string, options?: HttpRequest): Promise<HttpResponse> {
|
||||
return this.send({
|
||||
...options,
|
||||
method: "POST",
|
||||
url: url,
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
abstract send(request: HttpRequest): Promise<HttpResponse>;
|
||||
public abstract send(request: HttpRequest): Promise<HttpResponse>;
|
||||
}
|
||||
|
||||
export class DefaultHttpClient extends HttpClient {
|
||||
send(request: HttpRequest): Promise<HttpResponse> {
|
||||
public send(request: HttpRequest): Promise<HttpResponse> {
|
||||
return new Promise<HttpResponse>((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
const xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open(request.method, request.url, true);
|
||||
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||
|
|
@ -80,20 +80,19 @@ export class DefaultHttpClient extends HttpClient {
|
|||
}
|
||||
|
||||
if (xhr.status >= 200 && xhr.status < 300) {
|
||||
resolve(new HttpResponse(xhr.status, xhr.statusText, xhr.response || xhr.responseText))
|
||||
}
|
||||
else {
|
||||
resolve(new HttpResponse(xhr.status, xhr.statusText, xhr.response || xhr.responseText));
|
||||
} else {
|
||||
reject(new HttpError(xhr.statusText, xhr.status));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = () => {
|
||||
reject(new HttpError(xhr.statusText, xhr.status));
|
||||
}
|
||||
};
|
||||
|
||||
xhr.ontimeout = () => {
|
||||
reject(new TimeoutError());
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send(request.content || "");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { DataReceived, ConnectionClosed } from "./Common"
|
||||
import { IConnection } from "./IConnection"
|
||||
import { ITransport, TransferMode, TransportType, WebSocketTransport, ServerSentEventsTransport, LongPollingTransport } from "./Transports"
|
||||
import { HttpClient, DefaultHttpClient } from "./HttpClient"
|
||||
import { ILogger, LogLevel } from "./ILogger"
|
||||
import { LoggerFactory } from "./Loggers"
|
||||
import { ConnectionClosed, DataReceived } from "./Common";
|
||||
import { DefaultHttpClient, HttpClient } from "./HttpClient";
|
||||
import { IConnection } from "./IConnection";
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { LoggerFactory } from "./Loggers";
|
||||
import { ITransport, LongPollingTransport, ServerSentEventsTransport, TransferMode, TransportType, WebSocketTransport } from "./Transports";
|
||||
|
||||
export interface IHttpConnectionOptions {
|
||||
httpClient?: HttpClient;
|
||||
|
|
@ -18,12 +18,12 @@ export interface IHttpConnectionOptions {
|
|||
const enum ConnectionState {
|
||||
Connecting,
|
||||
Connected,
|
||||
Disconnected
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
interface INegotiateResponse {
|
||||
connectionId: string
|
||||
availableTransports: string[]
|
||||
connectionId: string;
|
||||
availableTransports: string[];
|
||||
}
|
||||
|
||||
export class HttpConnection implements IConnection {
|
||||
|
|
@ -37,7 +37,7 @@ export class HttpConnection implements IConnection {
|
|||
private connectionId: string;
|
||||
private startPromise: Promise<void>;
|
||||
|
||||
readonly features: any = {};
|
||||
public readonly features: any = {};
|
||||
|
||||
constructor(url: string, options: IHttpConnectionOptions = {}) {
|
||||
this.logger = LoggerFactory.createLogger(options.logger);
|
||||
|
|
@ -51,7 +51,7 @@ export class HttpConnection implements IConnection {
|
|||
this.options = options;
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
public async start(): Promise<void> {
|
||||
if (this.connectionState !== ConnectionState.Disconnected) {
|
||||
return Promise.reject(new Error("Cannot start a connection that is not in the 'Disconnected' state."));
|
||||
}
|
||||
|
|
@ -68,25 +68,24 @@ export class HttpConnection implements IConnection {
|
|||
// No need to add a connection ID in this case
|
||||
this.url = this.baseUrl;
|
||||
this.transport = this.createTransport(this.options.transport, [TransportType[TransportType.WebSockets]]);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
let headers;
|
||||
let token = this.options.accessTokenFactory();
|
||||
const token = this.options.accessTokenFactory();
|
||||
if (token) {
|
||||
headers = new Map<string, string>();
|
||||
headers.set("Authorization", `Bearer ${token}`);
|
||||
}
|
||||
|
||||
let negotiatePayload = await this.httpClient.post(this.resolveNegotiateUrl(this.baseUrl), {
|
||||
const negotiatePayload = await this.httpClient.post(this.resolveNegotiateUrl(this.baseUrl), {
|
||||
content: "",
|
||||
headers
|
||||
headers,
|
||||
});
|
||||
|
||||
let negotiateResponse: INegotiateResponse = JSON.parse(<string>negotiatePayload.content);
|
||||
const negotiateResponse: INegotiateResponse = JSON.parse(negotiatePayload.content as string);
|
||||
this.connectionId = negotiateResponse.connectionId;
|
||||
|
||||
// the user tries to stop the the connection when it is being started
|
||||
if (this.connectionState == ConnectionState.Disconnected) {
|
||||
if (this.connectionState === ConnectionState.Disconnected) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -97,9 +96,9 @@ export class HttpConnection implements IConnection {
|
|||
}
|
||||
|
||||
this.transport.onreceive = this.onreceive;
|
||||
this.transport.onclose = e => this.stopConnection(true, e);
|
||||
this.transport.onclose = (e) => this.stopConnection(true, e);
|
||||
|
||||
let requestedTransferMode =
|
||||
const requestedTransferMode =
|
||||
this.features.transferMode === TransferMode.Binary
|
||||
? TransferMode.Binary
|
||||
: TransferMode.Text;
|
||||
|
|
@ -109,13 +108,12 @@ export class HttpConnection implements IConnection {
|
|||
// only change the state if we were connecting to not overwrite
|
||||
// the state if the connection is already marked as Disconnected
|
||||
this.changeState(ConnectionState.Connecting, ConnectionState.Connected);
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
this.logger.log(LogLevel.Error, "Failed to start the connection. " + e);
|
||||
this.connectionState = ConnectionState.Disconnected;
|
||||
this.transport = null;
|
||||
throw e;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private createTransport(transport: TransportType | ITransport, availableTransports: string[]): ITransport {
|
||||
|
|
@ -143,36 +141,35 @@ export class HttpConnection implements IConnection {
|
|||
return typeof (transport) === "object" && "connect" in transport;
|
||||
}
|
||||
|
||||
private changeState(from: ConnectionState, to: ConnectionState): Boolean {
|
||||
if (this.connectionState == from) {
|
||||
private changeState(from: ConnectionState, to: ConnectionState): boolean {
|
||||
if (this.connectionState === from) {
|
||||
this.connectionState = to;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
send(data: any): Promise<void> {
|
||||
if (this.connectionState != ConnectionState.Connected) {
|
||||
public send(data: any): Promise<void> {
|
||||
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);
|
||||
}
|
||||
|
||||
async stop(error?: Error): Promise<void> {
|
||||
let previousState = this.connectionState;
|
||||
public async stop(error?: Error): Promise<void> {
|
||||
const previousState = this.connectionState;
|
||||
this.connectionState = ConnectionState.Disconnected;
|
||||
|
||||
try {
|
||||
await this.startPromise;
|
||||
}
|
||||
catch (e) {
|
||||
} catch (e) {
|
||||
// this exception is returned to the user as a rejected Promise from the start method
|
||||
}
|
||||
this.stopConnection(/*raiseClosed*/ previousState == ConnectionState.Connected, error);
|
||||
this.stopConnection(/*raiseClosed*/ previousState === ConnectionState.Connected, error);
|
||||
}
|
||||
|
||||
private stopConnection(raiseClosed: Boolean, error?: Error) {
|
||||
private stopConnection(raiseClosed: boolean, error?: Error) {
|
||||
if (this.transport) {
|
||||
this.transport.stop();
|
||||
this.transport = null;
|
||||
|
|
@ -197,28 +194,28 @@ export class HttpConnection implements IConnection {
|
|||
return url;
|
||||
}
|
||||
|
||||
if (typeof window === 'undefined' || !window || !window.document) {
|
||||
if (typeof window === "undefined" || !window || !window.document) {
|
||||
throw new Error(`Cannot resolve '${url}'.`);
|
||||
}
|
||||
|
||||
let parser = window.document.createElement("a");
|
||||
const parser = window.document.createElement("a");
|
||||
parser.href = url;
|
||||
|
||||
let baseUrl = (!parser.protocol || parser.protocol === ":")
|
||||
const baseUrl = (!parser.protocol || parser.protocol === ":")
|
||||
? `${window.document.location.protocol}//${(parser.host || window.document.location.host)}`
|
||||
: `${parser.protocol}//${parser.host}`;
|
||||
|
||||
if (!url || url[0] != '/') {
|
||||
url = '/' + url;
|
||||
if (!url || url[0] !== "/") {
|
||||
url = "/" + url;
|
||||
}
|
||||
|
||||
let normalizedUrl = baseUrl + url;
|
||||
const normalizedUrl = baseUrl + url;
|
||||
this.logger.log(LogLevel.Information, `Normalizing '${url}' to '${normalizedUrl}'`);
|
||||
return normalizedUrl;
|
||||
}
|
||||
|
||||
private resolveNegotiateUrl(url: string): string {
|
||||
let index = url.indexOf("?");
|
||||
const index = url.indexOf("?");
|
||||
let negotiateUrl = url.substring(0, index === -1 ? url.length : index);
|
||||
if (negotiateUrl[negotiateUrl.length - 1] !== "/") {
|
||||
negotiateUrl += "/";
|
||||
|
|
@ -228,6 +225,6 @@ export class HttpConnection implements IConnection {
|
|||
return negotiateUrl;
|
||||
}
|
||||
|
||||
onreceive: DataReceived;
|
||||
onclose: ConnectionClosed;
|
||||
public onreceive: DataReceived;
|
||||
public onclose: ConnectionClosed;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { ConnectionClosed } from "./Common"
|
||||
import { IConnection } from "./IConnection"
|
||||
import { HttpConnection, IHttpConnectionOptions } from "./HttpConnection"
|
||||
import { TransportType, TransferMode } from "./Transports"
|
||||
import { Subject, Observable } from "./Observable"
|
||||
import { IHubProtocol, ProtocolType, MessageType, HubMessage, CompletionMessage, StreamItemMessage, InvocationMessage, StreamInvocationMessage, NegotiationMessage, CancelInvocationMessage } from "./IHubProtocol";
|
||||
import { Base64EncodedHubProtocol } from "./Base64EncodedHubProtocol";
|
||||
import { ConnectionClosed } from "./Common";
|
||||
import { HttpConnection, IHttpConnectionOptions } from "./HttpConnection";
|
||||
import { IConnection } from "./IConnection";
|
||||
import { CancelInvocationMessage, CompletionMessage, HubMessage, IHubProtocol, InvocationMessage, MessageType, NegotiationMessage, ProtocolType, StreamInvocationMessage, StreamItemMessage } from "./IHubProtocol";
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { JsonHubProtocol } from "./JsonHubProtocol";
|
||||
import { TextMessageFormat } from "./TextMessageFormat"
|
||||
import { Base64EncodedHubProtocol } from "./Base64EncodedHubProtocol"
|
||||
import { ILogger, LogLevel } from "./ILogger"
|
||||
import { ConsoleLogger, NullLogger, LoggerFactory } from "./Loggers"
|
||||
import { ConsoleLogger, LoggerFactory, NullLogger } from "./Loggers";
|
||||
import { Observable, Subject } from "./Observable";
|
||||
import { TextMessageFormat } from "./TextMessageFormat";
|
||||
import { TransferMode, TransportType } from "./Transports";
|
||||
|
||||
export { JsonHubProtocol }
|
||||
export { JsonHubProtocol };
|
||||
|
||||
export interface IHubConnectionOptions extends IHttpConnectionOptions {
|
||||
protocol?: IHubProtocol;
|
||||
|
|
@ -27,7 +27,7 @@ export class HubConnection {
|
|||
private readonly logger: ILogger;
|
||||
private protocol: IHubProtocol;
|
||||
private callbacks: Map<string, (invocationEvent: StreamItemMessage | CompletionMessage, error?: Error) => void>;
|
||||
private methods: Map<string, ((...args: any[]) => void)[]>;
|
||||
private methods: Map<string, Array<(...args: any[]) => void>>;
|
||||
private id: number;
|
||||
private closedCallbacks: ConnectionClosed[];
|
||||
private timeoutHandle: NodeJS.Timer;
|
||||
|
|
@ -42,8 +42,7 @@ export class HubConnection {
|
|||
|
||||
if (typeof urlOrConnection === "string") {
|
||||
this.connection = new HttpConnection(urlOrConnection, options);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.connection = urlOrConnection;
|
||||
}
|
||||
|
||||
|
|
@ -54,7 +53,7 @@ export class HubConnection {
|
|||
this.connection.onclose = (error?: Error) => this.connectionClosed(error);
|
||||
|
||||
this.callbacks = new Map<string, (invocationEvent: HubMessage, error?: Error) => void>();
|
||||
this.methods = new Map<string, ((...args: any[]) => void)[]>();
|
||||
this.methods = new Map<string, Array<(...args: any[]) => void>>();
|
||||
this.closedCallbacks = [];
|
||||
this.id = 0;
|
||||
}
|
||||
|
|
@ -65,18 +64,16 @@ export class HubConnection {
|
|||
}
|
||||
|
||||
// Parse the messages
|
||||
let messages = this.protocol.parseMessages(data);
|
||||
|
||||
for (var i = 0; i < messages.length; ++i) {
|
||||
var message = messages[i];
|
||||
const messages = this.protocol.parseMessages(data);
|
||||
|
||||
for (const message of messages) {
|
||||
switch (message.type) {
|
||||
case MessageType.Invocation:
|
||||
this.invokeClientMethod(message);
|
||||
break;
|
||||
case MessageType.StreamItem:
|
||||
case MessageType.Completion:
|
||||
let callback = this.callbacks.get(message.invocationId);
|
||||
const callback = this.callbacks.get(message.invocationId);
|
||||
if (callback != null) {
|
||||
if (message.type === MessageType.Completion) {
|
||||
this.callbacks.delete(message.invocationId);
|
||||
|
|
@ -110,45 +107,44 @@ export class HubConnection {
|
|||
}
|
||||
|
||||
private invokeClientMethod(invocationMessage: InvocationMessage) {
|
||||
let methods = this.methods.get(invocationMessage.target.toLowerCase());
|
||||
const methods = this.methods.get(invocationMessage.target.toLowerCase());
|
||||
if (methods) {
|
||||
methods.forEach(m => m.apply(this, invocationMessage.arguments));
|
||||
methods.forEach((m) => m.apply(this, invocationMessage.arguments));
|
||||
if (invocationMessage.invocationId) {
|
||||
// This is not supported in v1. So we return an error to avoid blocking the server waiting for the response.
|
||||
let message = "Server requested a response, which is not supported in this version of the client."
|
||||
const message = "Server requested a response, which is not supported in this version of the client.";
|
||||
this.logger.log(LogLevel.Error, message);
|
||||
this.connection.stop(new Error(message))
|
||||
this.connection.stop(new Error(message));
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.logger.log(LogLevel.Warning, `No client method with the name '${invocationMessage.target}' found.`);
|
||||
}
|
||||
}
|
||||
|
||||
private connectionClosed(error?: Error) {
|
||||
this.callbacks.forEach(callback => {
|
||||
this.callbacks.forEach((callback) => {
|
||||
callback(undefined, error ? error : new Error("Invocation canceled due to connection being closed."));
|
||||
});
|
||||
this.callbacks.clear();
|
||||
|
||||
this.closedCallbacks.forEach(c => c.apply(this, [error]));
|
||||
this.closedCallbacks.forEach((c) => c.apply(this, [error]));
|
||||
|
||||
this.cleanupTimeout();
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
let requestedTransferMode =
|
||||
public async start(): Promise<void> {
|
||||
const requestedTransferMode =
|
||||
(this.protocol.type === ProtocolType.Binary)
|
||||
? TransferMode.Binary
|
||||
: TransferMode.Text;
|
||||
|
||||
this.connection.features.transferMode = requestedTransferMode
|
||||
this.connection.features.transferMode = requestedTransferMode;
|
||||
await this.connection.start();
|
||||
var actualTransferMode = this.connection.features.transferMode;
|
||||
const actualTransferMode = this.connection.features.transferMode;
|
||||
|
||||
await this.connection.send(
|
||||
TextMessageFormat.write(
|
||||
JSON.stringify(<NegotiationMessage>{ protocol: this.protocol.name })));
|
||||
JSON.stringify({ protocol: this.protocol.name } as NegotiationMessage)));
|
||||
|
||||
this.logger.log(LogLevel.Information, `Using HubProtocol '${this.protocol.name}'.`);
|
||||
|
||||
|
|
@ -159,21 +155,21 @@ export class HubConnection {
|
|||
this.configureTimeout();
|
||||
}
|
||||
|
||||
stop(): Promise<void> {
|
||||
public stop(): Promise<void> {
|
||||
this.cleanupTimeout();
|
||||
return this.connection.stop();
|
||||
}
|
||||
|
||||
stream<T>(methodName: string, ...args: any[]): Observable<T> {
|
||||
let invocationDescriptor = this.createStreamInvocation(methodName, args);
|
||||
public stream<T>(methodName: string, ...args: any[]): Observable<T> {
|
||||
const invocationDescriptor = this.createStreamInvocation(methodName, args);
|
||||
|
||||
let subject = new Subject<T>(() => {
|
||||
let cancelInvocation: CancelInvocationMessage = this.createCancelInvocation(invocationDescriptor.invocationId);
|
||||
let message: any = this.protocol.writeMessage(cancelInvocation);
|
||||
const subject = new Subject<T>(() => {
|
||||
const cancelInvocation: CancelInvocationMessage = this.createCancelInvocation(invocationDescriptor.invocationId);
|
||||
const cancelMessage: any = this.protocol.writeMessage(cancelInvocation);
|
||||
|
||||
this.callbacks.delete(invocationDescriptor.invocationId);
|
||||
|
||||
return this.connection.send(message);
|
||||
return this.connection.send(cancelMessage);
|
||||
});
|
||||
|
||||
this.callbacks.set(invocationDescriptor.invocationId, (invocationEvent: CompletionMessage | StreamItemMessage, error?: Error) => {
|
||||
|
|
@ -185,20 +181,18 @@ export class HubConnection {
|
|||
if (invocationEvent.type === MessageType.Completion) {
|
||||
if (invocationEvent.error) {
|
||||
subject.error(new Error(invocationEvent.error));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
subject.complete();
|
||||
}
|
||||
}
|
||||
else {
|
||||
subject.next(<T>(invocationEvent.item));
|
||||
} else {
|
||||
subject.next((invocationEvent.item) as T);
|
||||
}
|
||||
});
|
||||
|
||||
let message = this.protocol.writeMessage(invocationDescriptor);
|
||||
const message = this.protocol.writeMessage(invocationDescriptor);
|
||||
|
||||
this.connection.send(message)
|
||||
.catch(e => {
|
||||
.catch((e) => {
|
||||
subject.error(e);
|
||||
this.callbacks.delete(invocationDescriptor.invocationId);
|
||||
});
|
||||
|
|
@ -206,41 +200,39 @@ export class HubConnection {
|
|||
return subject;
|
||||
}
|
||||
|
||||
send(methodName: string, ...args: any[]): Promise<void> {
|
||||
let invocationDescriptor = this.createInvocation(methodName, args, true);
|
||||
public send(methodName: string, ...args: any[]): Promise<void> {
|
||||
const invocationDescriptor = this.createInvocation(methodName, args, true);
|
||||
|
||||
let message = this.protocol.writeMessage(invocationDescriptor);
|
||||
const message = this.protocol.writeMessage(invocationDescriptor);
|
||||
|
||||
return this.connection.send(message);
|
||||
}
|
||||
|
||||
invoke(methodName: string, ...args: any[]): Promise<any> {
|
||||
let invocationDescriptor = this.createInvocation(methodName, args, false);
|
||||
public invoke(methodName: string, ...args: any[]): Promise<any> {
|
||||
const invocationDescriptor = this.createInvocation(methodName, args, false);
|
||||
|
||||
let p = new Promise<any>((resolve, reject) => {
|
||||
const p = new Promise<any>((resolve, reject) => {
|
||||
this.callbacks.set(invocationDescriptor.invocationId, (invocationEvent: StreamItemMessage | CompletionMessage, error?: Error) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
if (invocationEvent.type === MessageType.Completion) {
|
||||
let completionMessage = <CompletionMessage>invocationEvent;
|
||||
const completionMessage = invocationEvent as CompletionMessage;
|
||||
if (completionMessage.error) {
|
||||
reject(new Error(completionMessage.error));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
resolve(completionMessage.result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
reject(new Error(`Unexpected message type: ${invocationEvent.type}`));
|
||||
}
|
||||
});
|
||||
|
||||
let message = this.protocol.writeMessage(invocationDescriptor);
|
||||
const message = this.protocol.writeMessage(invocationDescriptor);
|
||||
|
||||
this.connection.send(message)
|
||||
.catch(e => {
|
||||
.catch((e) => {
|
||||
reject(e);
|
||||
this.callbacks.delete(invocationDescriptor.invocationId);
|
||||
});
|
||||
|
|
@ -249,7 +241,7 @@ export class HubConnection {
|
|||
return p;
|
||||
}
|
||||
|
||||
on(methodName: string, method: (...args: any[]) => void) {
|
||||
public on(methodName: string, method: (...args: any[]) => void) {
|
||||
if (!methodName || !method) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -262,23 +254,23 @@ export class HubConnection {
|
|||
this.methods.get(methodName).push(method);
|
||||
}
|
||||
|
||||
off(methodName: string, method: (...args: any[]) => void) {
|
||||
public off(methodName: string, method: (...args: any[]) => void) {
|
||||
if (!methodName || !method) {
|
||||
return;
|
||||
}
|
||||
|
||||
methodName = methodName.toLowerCase();
|
||||
let handlers = this.methods.get(methodName);
|
||||
const handlers = this.methods.get(methodName);
|
||||
if (!handlers) {
|
||||
return;
|
||||
}
|
||||
var removeIdx = handlers.indexOf(method);
|
||||
if (removeIdx != -1) {
|
||||
const removeIdx = handlers.indexOf(method);
|
||||
if (removeIdx !== -1) {
|
||||
handlers.splice(removeIdx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
onclose(callback: ConnectionClosed) {
|
||||
public onclose(callback: ConnectionClosed) {
|
||||
if (callback) {
|
||||
this.closedCallbacks.push(callback);
|
||||
}
|
||||
|
|
@ -293,40 +285,39 @@ export class HubConnection {
|
|||
private createInvocation(methodName: string, args: any[], nonblocking: boolean): InvocationMessage {
|
||||
if (nonblocking) {
|
||||
return {
|
||||
type: MessageType.Invocation,
|
||||
target: methodName,
|
||||
arguments: args,
|
||||
target: methodName,
|
||||
type: MessageType.Invocation,
|
||||
};
|
||||
}
|
||||
else {
|
||||
let id = this.id;
|
||||
} else {
|
||||
const id = this.id;
|
||||
this.id++;
|
||||
|
||||
return {
|
||||
type: MessageType.Invocation,
|
||||
arguments: args,
|
||||
invocationId: id.toString(),
|
||||
target: methodName,
|
||||
arguments: args,
|
||||
type: MessageType.Invocation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private createStreamInvocation(methodName: string, args: any[]): StreamInvocationMessage {
|
||||
let id = this.id;
|
||||
const id = this.id;
|
||||
this.id++;
|
||||
|
||||
return {
|
||||
type: MessageType.StreamInvocation,
|
||||
arguments: args,
|
||||
invocationId: id.toString(),
|
||||
target: methodName,
|
||||
arguments: args,
|
||||
type: MessageType.StreamInvocation,
|
||||
};
|
||||
}
|
||||
|
||||
private createCancelInvocation(id: string): CancelInvocationMessage {
|
||||
return {
|
||||
type: MessageType.CancelInvocation,
|
||||
invocationId: id,
|
||||
type: MessageType.CancelInvocation,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { DataReceived, ConnectionClosed } from "./Common"
|
||||
import { TransportType, TransferMode, ITransport } from "./Transports"
|
||||
import { ConnectionClosed, DataReceived } from "./Common";
|
||||
import { ITransport, TransferMode, TransportType } from "./Transports";
|
||||
|
||||
export interface IConnection {
|
||||
readonly features: any;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export const enum MessageType {
|
|||
Ping = 6,
|
||||
}
|
||||
|
||||
export type MessageHeaders = { [key: string]: string };
|
||||
export interface MessageHeaders { [key: string]: string; }
|
||||
|
||||
export type HubMessage = InvocationMessage | StreamInvocationMessage | StreamItemMessage | CompletionMessage | CancelInvocationMessage | PingMessage;
|
||||
|
||||
|
|
@ -26,13 +26,13 @@ export interface HubInvocationMessage extends HubMessageBase {
|
|||
export interface InvocationMessage extends HubInvocationMessage {
|
||||
readonly type: MessageType.Invocation;
|
||||
readonly target: string;
|
||||
readonly arguments: Array<any>;
|
||||
readonly arguments: any[];
|
||||
}
|
||||
|
||||
export interface StreamInvocationMessage extends HubInvocationMessage {
|
||||
readonly type: MessageType.StreamInvocation;
|
||||
readonly target: string;
|
||||
readonly arguments: Array<any>
|
||||
readonly arguments: any[];
|
||||
}
|
||||
|
||||
export interface StreamItemMessage extends HubInvocationMessage {
|
||||
|
|
@ -60,7 +60,7 @@ export interface CancelInvocationMessage extends HubInvocationMessage {
|
|||
|
||||
export const enum ProtocolType {
|
||||
Text = 1,
|
||||
Binary
|
||||
Binary,
|
||||
}
|
||||
|
||||
export interface IHubProtocol {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export enum LogLevel {
|
|||
Information,
|
||||
Warning,
|
||||
Error,
|
||||
None
|
||||
None,
|
||||
}
|
||||
|
||||
export interface ILogger {
|
||||
|
|
|
|||
|
|
@ -1,33 +1,33 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { HubMessage, IHubProtocol, ProtocolType } from "./IHubProtocol";
|
||||
import { TextMessageFormat } from "./TextMessageFormat";
|
||||
import { IHubProtocol, ProtocolType, HubMessage } from "./IHubProtocol";
|
||||
|
||||
export const JSON_HUB_PROTOCOL_NAME: string = "json";
|
||||
|
||||
export class JsonHubProtocol implements IHubProtocol {
|
||||
|
||||
readonly name: string = JSON_HUB_PROTOCOL_NAME;
|
||||
public readonly name: string = JSON_HUB_PROTOCOL_NAME;
|
||||
|
||||
readonly type: ProtocolType = ProtocolType.Text;
|
||||
public readonly type: ProtocolType = ProtocolType.Text;
|
||||
|
||||
parseMessages(input: string): HubMessage[] {
|
||||
public parseMessages(input: string): HubMessage[] {
|
||||
if (!input) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Parse the messages
|
||||
let messages = TextMessageFormat.parse(input);
|
||||
let hubMessages = [];
|
||||
for (var i = 0; i < messages.length; ++i) {
|
||||
hubMessages.push(JSON.parse(messages[i]));
|
||||
const messages = TextMessageFormat.parse(input);
|
||||
const hubMessages = [];
|
||||
for (const message of messages) {
|
||||
hubMessages.push(JSON.parse(message));
|
||||
}
|
||||
|
||||
return hubMessages;
|
||||
}
|
||||
|
||||
writeMessage(message: HubMessage): string {
|
||||
public writeMessage(message: HubMessage): string {
|
||||
return TextMessageFormat.write(JSON.stringify(message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { ILogger, LogLevel } from "./ILogger"
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
|
||||
export class NullLogger implements ILogger {
|
||||
log(logLevel: LogLevel, message: string): void {
|
||||
public log(logLevel: LogLevel, message: string): void {
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ export class ConsoleLogger implements ILogger {
|
|||
this.minimumLogLevel = minimumLogLevel;
|
||||
}
|
||||
|
||||
log(logLevel: LogLevel, message: string): void {
|
||||
public log(logLevel: LogLevel, message: string): void {
|
||||
if (logLevel >= this.minimumLogLevel) {
|
||||
switch (logLevel) {
|
||||
case LogLevel.Error:
|
||||
|
|
@ -35,8 +35,8 @@ export class ConsoleLogger implements ILogger {
|
|||
}
|
||||
}
|
||||
|
||||
export namespace LoggerFactory {
|
||||
export function createLogger(logging?: ILogger | LogLevel) {
|
||||
export class LoggerFactory {
|
||||
public static createLogger(logging?: ILogger | LogLevel) {
|
||||
if (logging === undefined) {
|
||||
return new ConsoleLogger(LogLevel.Information);
|
||||
}
|
||||
|
|
@ -45,10 +45,10 @@ export namespace LoggerFactory {
|
|||
return new NullLogger();
|
||||
}
|
||||
|
||||
if ((<ILogger>logging).log) {
|
||||
return <ILogger>logging;
|
||||
if ((logging as ILogger).log) {
|
||||
return logging as ILogger;
|
||||
}
|
||||
|
||||
return new ConsoleLogger(<LogLevel>logging);
|
||||
return new ConsoleLogger(logging as LogLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export interface Observer<T> {
|
|||
}
|
||||
|
||||
export class Subscription<T> {
|
||||
subject: Subject<T>;
|
||||
observer: Observer<T>;
|
||||
private subject: Subject<T>;
|
||||
private observer: Observer<T>;
|
||||
|
||||
constructor(subject: Subject<T>, observer: Observer<T>) {
|
||||
this.subject = subject;
|
||||
|
|
@ -20,7 +20,7 @@ export class Subscription<T> {
|
|||
}
|
||||
|
||||
public dispose(): void {
|
||||
let index: number = this.subject.observers.indexOf(this.observer);
|
||||
const index: number = this.subject.observers.indexOf(this.observer);
|
||||
if (index > -1) {
|
||||
this.subject.observers.splice(index, 1);
|
||||
}
|
||||
|
|
@ -36,8 +36,8 @@ export interface Observable<T> {
|
|||
}
|
||||
|
||||
export class Subject<T> implements Observable<T> {
|
||||
observers: Observer<T>[];
|
||||
cancelCallback: () => Promise<void>;
|
||||
public observers: Array<Observer<T>>;
|
||||
public cancelCallback: () => Promise<void>;
|
||||
|
||||
constructor(cancelCallback: () => Promise<void>) {
|
||||
this.observers = [];
|
||||
|
|
@ -45,13 +45,13 @@ export class Subject<T> implements Observable<T> {
|
|||
}
|
||||
|
||||
public next(item: T): void {
|
||||
for (let observer of this.observers) {
|
||||
for (const observer of this.observers) {
|
||||
observer.next(item);
|
||||
}
|
||||
}
|
||||
|
||||
public error(err: any): void {
|
||||
for (let observer of this.observers) {
|
||||
for (const observer of this.observers) {
|
||||
if (observer.error) {
|
||||
observer.error(err);
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ export class Subject<T> implements Observable<T> {
|
|||
}
|
||||
|
||||
public complete(): void {
|
||||
for (let observer of this.observers) {
|
||||
for (const observer of this.observers) {
|
||||
if (observer.complete) {
|
||||
observer.complete();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
export class TextMessageFormat {
|
||||
static RecordSeparator = String.fromCharCode(0x1e);
|
||||
private static RecordSeparator = String.fromCharCode(0x1e);
|
||||
|
||||
static write(output: string): string {
|
||||
public static write(output: string): string {
|
||||
return `${output}${TextMessageFormat.RecordSeparator}`;
|
||||
}
|
||||
|
||||
static parse(input: string): string[] {
|
||||
if (input[input.length - 1] != TextMessageFormat.RecordSeparator) {
|
||||
public static parse(input: string): string[] {
|
||||
if (input[input.length - 1] !== TextMessageFormat.RecordSeparator) {
|
||||
throw new Error("Message is incomplete.");
|
||||
}
|
||||
|
||||
let messages = input.split(TextMessageFormat.RecordSeparator);
|
||||
const messages = input.split(TextMessageFormat.RecordSeparator);
|
||||
messages.pop();
|
||||
return messages;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { DataReceived, TransportClosed } from "./Common";
|
||||
import { HttpClient, HttpRequest } from "./HttpClient";
|
||||
import { HttpError, TimeoutError } from "./Errors";
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { IConnection } from "./IConnection";
|
||||
import { AbortController } from "./AbortController";
|
||||
import { DataReceived, TransportClosed } from "./Common";
|
||||
import { HttpError, TimeoutError } from "./Errors";
|
||||
import { HttpClient, HttpRequest } from "./HttpClient";
|
||||
import { IConnection } from "./IConnection";
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
|
||||
export enum TransportType {
|
||||
WebSockets,
|
||||
ServerSentEvents,
|
||||
LongPolling
|
||||
LongPolling,
|
||||
}
|
||||
|
||||
export const enum TransferMode {
|
||||
Text = 1,
|
||||
Binary
|
||||
Binary,
|
||||
}
|
||||
|
||||
export interface ITransport {
|
||||
|
|
@ -37,17 +37,17 @@ export class WebSocketTransport implements ITransport {
|
|||
this.accessTokenFactory = accessTokenFactory || (() => null);
|
||||
}
|
||||
|
||||
connect(url: string, requestedTransferMode: TransferMode, connection: IConnection): Promise<TransferMode> {
|
||||
public connect(url: string, requestedTransferMode: TransferMode, connection: IConnection): Promise<TransferMode> {
|
||||
|
||||
return new Promise<TransferMode>((resolve, reject) => {
|
||||
url = url.replace(/^http/, "ws");
|
||||
let token = this.accessTokenFactory();
|
||||
const token = this.accessTokenFactory();
|
||||
if (token) {
|
||||
url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
|
||||
}
|
||||
|
||||
let webSocket = new WebSocket(url);
|
||||
if (requestedTransferMode == TransferMode.Binary) {
|
||||
const webSocket = new WebSocket(url);
|
||||
if (requestedTransferMode === TransferMode.Binary) {
|
||||
webSocket.binaryType = "arraybuffer";
|
||||
}
|
||||
|
||||
|
|
@ -66,23 +66,22 @@ export class WebSocketTransport implements ITransport {
|
|||
if (this.onreceive) {
|
||||
this.onreceive(message.data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
webSocket.onclose = (event: CloseEvent) => {
|
||||
// webSocket will be null if the transport did not start successfully
|
||||
if (this.onclose && this.webSocket) {
|
||||
if (event.wasClean === false || event.code !== 1000) {
|
||||
this.onclose(new Error(`Websocket closed with status code: ${event.code} (${event.reason})`));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
this.onclose();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
send(data: any): Promise<void> {
|
||||
public send(data: any): Promise<void> {
|
||||
if (this.webSocket && this.webSocket.readyState === WebSocket.OPEN) {
|
||||
this.webSocket.send(data);
|
||||
return Promise.resolve();
|
||||
|
|
@ -91,7 +90,7 @@ export class WebSocketTransport implements ITransport {
|
|||
return Promise.reject("WebSocket is not in the OPEN state");
|
||||
}
|
||||
|
||||
stop(): Promise<void> {
|
||||
public stop(): Promise<void> {
|
||||
if (this.webSocket) {
|
||||
this.webSocket.close();
|
||||
this.webSocket = null;
|
||||
|
|
@ -99,8 +98,8 @@ export class WebSocketTransport implements ITransport {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
onreceive: DataReceived;
|
||||
onclose: TransportClosed;
|
||||
public onreceive: DataReceived;
|
||||
public onclose: TransportClosed;
|
||||
}
|
||||
|
||||
export class ServerSentEventsTransport implements ITransport {
|
||||
|
|
@ -116,19 +115,19 @@ export class ServerSentEventsTransport implements ITransport {
|
|||
this.logger = logger;
|
||||
}
|
||||
|
||||
connect(url: string, requestedTransferMode: TransferMode, connection: IConnection): Promise<TransferMode> {
|
||||
public connect(url: string, requestedTransferMode: TransferMode, connection: IConnection): Promise<TransferMode> {
|
||||
if (typeof (EventSource) === "undefined") {
|
||||
Promise.reject("EventSource not supported by the browser.");
|
||||
}
|
||||
|
||||
this.url = url;
|
||||
return new Promise<TransferMode>((resolve, reject) => {
|
||||
let token = this.accessTokenFactory();
|
||||
const token = this.accessTokenFactory();
|
||||
if (token) {
|
||||
url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
|
||||
}
|
||||
|
||||
let eventSource = new EventSource(url);
|
||||
const eventSource = new EventSource(url);
|
||||
|
||||
try {
|
||||
eventSource.onmessage = (e: MessageEvent) => {
|
||||
|
|
@ -152,26 +151,25 @@ export class ServerSentEventsTransport implements ITransport {
|
|||
if (this.eventSource && this.onclose) {
|
||||
this.onclose(new Error(e.message || "Error occurred"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
eventSource.onopen = () => {
|
||||
this.logger.log(LogLevel.Information, `SSE connected to ${this.url}`);
|
||||
this.eventSource = eventSource;
|
||||
// SSE is a text protocol
|
||||
resolve(TransferMode.Text);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
};
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async send(data: any): Promise<void> {
|
||||
public async send(data: any): Promise<void> {
|
||||
return send(this.httpClient, this.url, this.accessTokenFactory, data);
|
||||
}
|
||||
|
||||
stop(): Promise<void> {
|
||||
public stop(): Promise<void> {
|
||||
if (this.eventSource) {
|
||||
this.eventSource.close();
|
||||
this.eventSource = null;
|
||||
|
|
@ -179,8 +177,8 @@ export class ServerSentEventsTransport implements ITransport {
|
|||
return Promise.resolve();
|
||||
}
|
||||
|
||||
onreceive: DataReceived;
|
||||
onclose: TransportClosed;
|
||||
public onreceive: DataReceived;
|
||||
public onclose: TransportClosed;
|
||||
}
|
||||
|
||||
export class LongPollingTransport implements ITransport {
|
||||
|
|
@ -199,7 +197,7 @@ export class LongPollingTransport implements ITransport {
|
|||
this.pollAbort = new AbortController();
|
||||
}
|
||||
|
||||
connect(url: string, requestedTransferMode: TransferMode, connection: IConnection): Promise<TransferMode> {
|
||||
public connect(url: string, requestedTransferMode: TransferMode, connection: IConnection): Promise<TransferMode> {
|
||||
this.url = url;
|
||||
|
||||
// Set a flag indicating we have inherent keep-alive in this transport.
|
||||
|
|
@ -215,26 +213,26 @@ export class LongPollingTransport implements ITransport {
|
|||
}
|
||||
|
||||
private async poll(url: string, transferMode: TransferMode): Promise<void> {
|
||||
let pollOptions: HttpRequest = {
|
||||
timeout: 120000,
|
||||
const pollOptions: HttpRequest = {
|
||||
abortSignal: this.pollAbort.signal,
|
||||
headers: new Map<string, string>(),
|
||||
timeout: 120000,
|
||||
};
|
||||
|
||||
if (transferMode === TransferMode.Binary) {
|
||||
pollOptions.responseType = "arraybuffer";
|
||||
}
|
||||
|
||||
let token = this.accessTokenFactory();
|
||||
const token = this.accessTokenFactory();
|
||||
if (token) {
|
||||
pollOptions.headers.set("Authorization", `Bearer ${token}`);
|
||||
}
|
||||
|
||||
while (!this.pollAbort.signal.aborted) {
|
||||
try {
|
||||
let pollUrl = `${url}&_=${Date.now()}`;
|
||||
const pollUrl = `${url}&_=${Date.now()}`;
|
||||
this.logger.log(LogLevel.Trace, `(LongPolling transport) polling: ${pollUrl}`);
|
||||
let response = await this.httpClient.get(pollUrl, pollOptions)
|
||||
const response = await this.httpClient.get(pollUrl, pollOptions);
|
||||
if (response.statusCode === 204) {
|
||||
this.logger.log(LogLevel.Information, "(LongPolling transport) Poll terminated by server");
|
||||
|
||||
|
|
@ -243,8 +241,7 @@ export class LongPollingTransport implements ITransport {
|
|||
this.onclose();
|
||||
}
|
||||
this.pollAbort.abort();
|
||||
}
|
||||
else if (response.statusCode !== 200) {
|
||||
} else if (response.statusCode !== 200) {
|
||||
this.logger.log(LogLevel.Error, `(LongPolling transport) Unexpected response code: ${response.statusCode}`);
|
||||
|
||||
// Unexpected status code
|
||||
|
|
@ -252,16 +249,14 @@ export class LongPollingTransport implements ITransport {
|
|||
this.onclose(new HttpError(response.statusText, response.statusCode));
|
||||
}
|
||||
this.pollAbort.abort();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Process the response
|
||||
if (response.content) {
|
||||
this.logger.log(LogLevel.Trace, `(LongPolling transport) data received: ${response.content}`);
|
||||
if (this.onreceive) {
|
||||
this.onreceive(response.content);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// This is another way timeout manifest.
|
||||
this.logger.log(LogLevel.Trace, "(LongPolling transport) Poll timed out, reissuing.");
|
||||
}
|
||||
|
|
@ -281,29 +276,29 @@ export class LongPollingTransport implements ITransport {
|
|||
}
|
||||
}
|
||||
|
||||
async send(data: any): Promise<void> {
|
||||
public async send(data: any): Promise<void> {
|
||||
return send(this.httpClient, this.url, this.accessTokenFactory, data);
|
||||
}
|
||||
|
||||
stop(): Promise<void> {
|
||||
public stop(): Promise<void> {
|
||||
this.pollAbort.abort();
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
onreceive: DataReceived;
|
||||
onclose: TransportClosed;
|
||||
public onreceive: DataReceived;
|
||||
public onclose: TransportClosed;
|
||||
}
|
||||
|
||||
async function send(httpClient: HttpClient, url: string, accessTokenFactory: () => string, content: string | ArrayBuffer): Promise<void> {
|
||||
let headers;
|
||||
let token = accessTokenFactory();
|
||||
const token = accessTokenFactory();
|
||||
if (token) {
|
||||
headers = new Map<string, string>();
|
||||
headers.set("Authorization", `Bearer ${accessTokenFactory()}`)
|
||||
headers.set("Authorization", `Bearer ${accessTokenFactory()}`);
|
||||
}
|
||||
|
||||
await httpClient.post(url, {
|
||||
content,
|
||||
headers
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,6 @@
|
|||
|
||||
// This is where we add any polyfills we'll need for the browser. It is the entry module for browser-specific builds.
|
||||
|
||||
import 'es6-promise/dist/es6-promise.auto.js';
|
||||
import "es6-promise/dist/es6-promise.auto.js";
|
||||
|
||||
export * from './index'
|
||||
export * from "./index";
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
// Everything that users need to access must be exported here. Including interfaces.
|
||||
export * from "./Common"
|
||||
export * from "./Errors"
|
||||
export * from "./HttpClient"
|
||||
export * from "./HttpConnection"
|
||||
export * from "./HubConnection"
|
||||
export * from "./IConnection"
|
||||
export * from "./IHubProtocol"
|
||||
export * from "./ILogger"
|
||||
export * from "./Loggers"
|
||||
export * from "./Transports"
|
||||
export * from "./Observable"
|
||||
export * from "./Common";
|
||||
export * from "./Errors";
|
||||
export * from "./HttpClient";
|
||||
export * from "./HttpConnection";
|
||||
export * from "./HubConnection";
|
||||
export * from "./IConnection";
|
||||
export * from "./IHubProtocol";
|
||||
export * from "./ILogger";
|
||||
export * from "./Loggers";
|
||||
export * from "./Transports";
|
||||
export * from "./Observable";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rules": {
|
||||
"max-line-length": { "options": [300] },
|
||||
"member-ordering": false,
|
||||
"interface-name": false,
|
||||
"unified-signatures": false,
|
||||
"max-classes-per-file": false,
|
||||
"no-empty": false,
|
||||
"no-bitwise": false,
|
||||
"no-console": false
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue