Add TSLint rule file and fix all violations (#1381)

This commit is contained in:
BrennanConroy 2018-02-07 15:26:13 -08:00 committed by GitHub
parent f08da32eb2
commit 331bf3515d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1132 additions and 937 deletions

View File

@ -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"
},

View File

@ -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);
});
});
}
}

View File

@ -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();
});

View File

@ -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));
}
};
});
}
});

View File

@ -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);
}

View File

@ -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";

View File

@ -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",

View File

@ -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"
}

View File

@ -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": {

View File

@ -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",

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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";

View File

@ -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";

View File

@ -1,6 +1,6 @@
{
"name": "@aspnet/signalr",
"version": "1.0.0-preview1-t000",
"version": "1.0.0-preview2-t000",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -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",

View File

@ -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);
})
})
});
});
});

View File

@ -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;

View File

@ -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]));
}

View File

@ -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);
})
});
});
});

View File

@ -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);
});

View File

@ -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);
}
};
}

View File

@ -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);
});
});
});

View File

@ -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);
}
}

View File

@ -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);
});
});
});
});

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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};`;
}
}
}

View File

@ -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;

View File

@ -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 || "");
});

View File

@ -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;
}

View File

@ -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,
};
}
}

View File

@ -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;

View File

@ -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 {

View File

@ -6,7 +6,7 @@ export enum LogLevel {
Information,
Warning,
Error,
None
None,
}
export interface ILogger {

View File

@ -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));
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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,
});
}

View File

@ -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";

View File

@ -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";

13
client-ts/tslint.json Normal file
View File

@ -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
}
}