Making the client and tests work on IE

This commit is contained in:
Pawel Kadluczka 2017-08-28 14:56:56 -07:00
parent ff2bf5ad75
commit 2a36aa141d
9 changed files with 180 additions and 218 deletions

View File

@ -15,7 +15,7 @@ export class Base64EncodedHubProtocol implements IHubProtocol {
parseMessages(input: any): HubMessage[] { parseMessages(input: any): HubMessage[] {
// The format of the message is `size:message;` // The format of the message is `size:message;`
let pos = input.indexOf(":"); let pos = input.indexOf(":");
if (pos == -1 || !input.endsWith(";")) { if (pos == -1 || input[input.length - 1] != ';') {
throw new Error("Invalid payload."); throw new Error("Invalid payload.");
} }

View File

@ -8,7 +8,7 @@ export namespace TextMessageFormat {
} }
export function parse(input: string): string[] { export function parse(input: string): string[] {
if (!input.endsWith(RecordSeparator)) { if (input[input.length - 1] != RecordSeparator) {
throw new Error("Message is incomplete."); throw new Error("Message is incomplete.");
} }
@ -57,7 +57,10 @@ export namespace BinaryMessageFormat {
} }
if (uint8Array.byteLength >= (offset + 8 + size)) { if (uint8Array.byteLength >= (offset + 8 + size)) {
result.push(uint8Array.slice(offset + 8, offset + 8 + size)) // IE does not support .slice() so use subarray
result.push(uint8Array.slice
? uint8Array.slice(offset + 8, offset + 8 + size)
: uint8Array.subarray(offset + 8, offset + 8 + size));
} }
else { else {
throw new Error("Incomplete message"); throw new Error("Incomplete message");

View File

@ -55,11 +55,10 @@ export class HubConnection {
case MessageType.Completion: case MessageType.Completion:
let callback = this.callbacks.get(message.invocationId); let callback = this.callbacks.get(message.invocationId);
if (callback != null) { if (callback != null) {
callback(message);
if (message.type == MessageType.Completion) { if (message.type == MessageType.Completion) {
this.callbacks.delete(message.invocationId); this.callbacks.delete(message.invocationId);
} }
callback(message);
} }
break; break;
default: default:

View File

@ -195,9 +195,6 @@ export class LongPollingTransport implements ITransport {
} }
let pollXhr = new XMLHttpRequest(); let pollXhr = new XMLHttpRequest();
if (transferMode === TransferMode.Binary) {
pollXhr.responseType = "arraybuffer";
}
pollXhr.onload = () => { pollXhr.onload = () => {
if (pollXhr.status == 200) { if (pollXhr.status == 200) {
@ -248,6 +245,11 @@ export class LongPollingTransport implements ITransport {
this.pollXhr = pollXhr; this.pollXhr = pollXhr;
this.pollXhr.open("GET", url, true); this.pollXhr.open("GET", url, true);
if (transferMode === TransferMode.Binary) {
this.pollXhr.responseType = "arraybuffer";
}
// IE caches xhr requests
this.pollXhr.setRequestHeader("Cache-Control", "no-cache");
// TODO: consider making timeout configurable // TODO: consider making timeout configurable
this.pollXhr.timeout = 120000; this.pollXhr.timeout = 120000;
this.pollXhr.send(); this.pollXhr.send();

View File

@ -8,8 +8,8 @@
<script type="text/javascript" src="lib/jasmine/jasmine.js"></script> <script type="text/javascript" src="lib/jasmine/jasmine.js"></script>
<script type="text/javascript" src="lib/jasmine/jasmine-html.js"></script> <script type="text/javascript" src="lib/jasmine/jasmine-html.js"></script>
<script type="text/javascript" src="lib/jasmine/boot.js"></script> <script type="text/javascript" src="lib/jasmine/boot.js"></script>
<script type="text/javascript" src="lib/signalr/signalr-client.min.js"></script> <script type="text/javascript" src="lib/signalr/signalr-clientES5.min.js"></script>
<script type="text/javascript" src="lib/signalr/signalr-msgpackprotocol.min.js"></script> <script type="text/javascript" src="lib/signalr/signalr-msgpackprotocolES5.min.js"></script>
<script src="js/common.js"></script> <script src="js/common.js"></script>
<script src="js/webSocketTests.js"></script> <script src="js/webSocketTests.js"></script>
<script src="js/connectionTests.js"></script> <script src="js/connectionTests.js"></script>

View File

@ -1,8 +1,10 @@
const ECHOENDPOINT_URL = `http://${document.location.host}/echo`; "use strict";
var ECHOENDPOINT_URL = "http://" + document.location.host + "/echo";
function getTransportTypes() { function getTransportTypes() {
let transportTypes = [ signalR.TransportType.WebSockets ]; var transportTypes = [signalR.TransportType.WebSockets];
if (typeof (EventSource) !== "undefined") { if (typeof EventSource !== "undefined") {
transportTypes.push(signalR.TransportType.ServerSentEvents); transportTypes.push(signalR.TransportType.ServerSentEvents);
} }
transportTypes.push(signalR.TransportType.LongPolling); transportTypes.push(signalR.TransportType.LongPolling);
@ -11,14 +13,16 @@ function getTransportTypes() {
} }
function eachTransport(action) { function eachTransport(action) {
getTransportTypes().forEach(t => action(t)); getTransportTypes().forEach(function (t) {
return action(t);
});
} }
function eachTransportAndProtocol(action) { function eachTransportAndProtocol(action) {
let protocols = [ var protocols = [new signalR.JsonHubProtocol(), new signalRMsgPack.MessagePackHubProtocol()];
new signalR.JsonHubProtocol(), getTransportTypes().forEach(function (t) {
new signalRMsgPack.MessagePackHubProtocol() return protocols.forEach(function (p) {
]; return action(t, p);
getTransportTypes().forEach(t => });
protocols.forEach(p => action(t, p))); });
} }

View File

@ -1,61 +1,58 @@
describe('connection', () => { "use strict";
it(`can connect to the server without specifying transport explicitly`, done => {
const message = "Hello World!";
let connection = new signalR.HttpConnection(ECHOENDPOINT_URL);
let received = ""; describe('connection', function () {
connection.onDataReceived = data => { it("can connect to the server without specifying transport explicitly", function (done) {
var message = "Hello World!";
var connection = new signalR.HttpConnection(ECHOENDPOINT_URL);
var received = "";
connection.onDataReceived = function (data) {
received += data; received += data;
if (data == message) { if (data == message) {
connection.stop(); connection.stop();
} }
} };
connection.onClosed = error => { connection.onClosed = function (error) {
expect(error).toBeUndefined(); expect(error).toBeUndefined();
done(); done();
} };
connection.start() connection.start().then(function () {
.then(() => { connection.send(message);
connection.send(message); }).catch(function (e) {
}) fail();
.catch(e => { done();
fail(); });
done();
});
}); });
eachTransport(transportType => { eachTransport(function (transportType) {
it(`over ${signalR.TransportType[transportType]} can send and receive messages`, done => { it("over " + signalR.TransportType[transportType] + " can send and receive messages", function (done) {
const message = "Hello World!"; var message = "Hello World!";
let connection = new signalR.HttpConnection(ECHOENDPOINT_URL, var connection = new signalR.HttpConnection(ECHOENDPOINT_URL, {
{ transport: transportType,
transport: transportType, logger: new signalR.ConsoleLogger(signalR.LogLevel.Information)
logger: new signalR.ConsoleLogger(signalR.LogLevel.Information) });
});
let received = ""; var received = "";
connection.onDataReceived = data => { connection.onDataReceived = function (data) {
received += data; received += data;
if (data == message) { if (data == message) {
connection.stop(); connection.stop();
} }
} };
connection.onClosed = error => { connection.onClosed = function (error) {
expect(error).toBeUndefined(); expect(error).toBeUndefined();
done(); done();
} };
connection.start() connection.start().then(function () {
.then(() => { connection.send(message);
connection.send(message); }).catch(function (e) {
}) fail();
.catch(e => { done();
fail(); });
done();
});
}); });
}); });
}); });

View File

@ -1,194 +1,149 @@
const TESTHUBENDPOINT_URL = `http://${document.location.host}/testhub`; 'use strict';
describe('hubConnection', () => { var TESTHUBENDPOINT_URL = 'http://' + document.location.host + '/testhub';
eachTransportAndProtocol((transportType, protocol) => {
describe(`${protocol.name} over ${signalR.TransportType[transportType]} transport`, () => { describe('hubConnection', function () {
it(`can invoke server method and receive result`, done => { eachTransportAndProtocol(function (transportType, protocol) {
const message = "你好,世界!"; describe(protocol.name + ' over ' + signalR.TransportType[transportType] + ' transport', function () {
let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection( it('can invoke server method and receive result', function (done) {
TESTHUBENDPOINT_URL, var message = "你好,世界!";
{ transport: transportType, logger: logger }), var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
logger, var hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType, logger: logger }), logger, protocol);
protocol); hubConnection.onClosed = function (error) {
hubConnection.onClosed = error => {
expect(error).toBe(undefined); expect(error).toBe(undefined);
done(); done();
} };
hubConnection.start() hubConnection.start().then(function () {
.then(() => { hubConnection.invoke('Echo', message).then(function (result) {
hubConnection.invoke('Echo', message) expect(result).toBe(message);
.then(result => { }).catch(function (e) {
expect(result).toBe(message);
})
.catch(e => {
fail(e);
})
.then(() => {
hubConnection.stop();
})
})
.catch(e => {
fail(e); fail(e);
done(); }).then(function () {
hubConnection.stop();
}); });
}).catch(function (e) {
fail(e);
done();
});
}); });
it(`can stream server method and receive result`, done => { it('can stream server method and receive result', function (done) {
let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information); var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection( var hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType, logger: logger }), logger, protocol);
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.onClosed = error => { hubConnection.onClosed = function (error) {
expect(error).toBe(undefined); expect(error).toBe(undefined);
done(); done();
} };
let received = []; var received = [];
hubConnection.start() hubConnection.start().then(function () {
.then(() => { hubConnection.stream('Stream').subscribe({
hubConnection.stream('Stream') next: function next(item) {
.subscribe({ received.push(item);
next: (item) => { },
received.push(item); error: function error(err) {
}, fail(err);
error: (err) => { hubConnection.stop();
fail(err); },
hubConnection.stop(); complete: function complete() {
}, expect(received).toEqual(["a", "b", "c"]);
complete: () => { hubConnection.stop();
expect(received).toEqual(["a", "b", "c"]); }
hubConnection.stop();
}
});
})
.catch(e => {
fail(e);
done();
}); });
}).catch(function (e) {
fail(e);
done();
});
}); });
it(`rethrows an exception from the server when invoking`, done => { it('rethrows an exception from the server when invoking', function (done) {
const errorMessage = "An error occurred."; var errorMessage = "An error occurred.";
let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information); var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection( var hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType, logger: logger }), logger, protocol);
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.start() hubConnection.start().then(function () {
.then(() => { hubConnection.invoke('ThrowException', errorMessage).then(function () {
hubConnection.invoke('ThrowException', errorMessage) // exception expected but none thrown
.then(() => { fail();
// exception expected but none thrown }).catch(function (e) {
fail(); expect(e.message).toBe(errorMessage);
}) }).then(function () {
.catch(e => { return hubConnection.stop();
expect(e.message).toBe(errorMessage); }).then(function () {
})
.then(() => {
return hubConnection.stop();
})
.then(() => {
done();
});
})
.catch(e => {
fail(e);
done(); done();
}); });
}).catch(function (e) {
fail(e);
done();
});
}); });
it(`rethrows an exception from the server when streaming`, done => { it('rethrows an exception from the server when streaming', function (done) {
const errorMessage = "An error occurred."; var errorMessage = "An error occurred.";
let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information); var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection( var hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType, logger: logger }), logger, protocol);
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.start() hubConnection.start().then(function () {
.then(() => { hubConnection.stream('ThrowException', errorMessage).subscribe({
hubConnection.stream('ThrowException', errorMessage) next: function next(item) {
.subscribe({ fail();
next: (item) => { },
fail(); error: function error(err) {
}, expect(err.message).toEqual("An error occurred.");
error: (err) => { done();
expect(err.message).toEqual("An error occurred."); },
done(); complete: function complete() {
}, fail();
complete: () => { }
fail();
}
});
})
.catch(e => {
fail(e);
done();
}); });
}).catch(function (e) {
fail(e);
done();
});
}); });
it(`can receive server calls`, done => { it('can receive server calls', function (done) {
let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information); var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection( var hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType, logger: logger }), logger, protocol);
TESTHUBENDPOINT_URL,
{ transport: transportType, logger: logger }),
logger,
protocol);
const message = "你好 SignalR"; var message = "你好 SignalR";
let callbackPromise = new Promise((resolve, reject) => { hubConnection.on("Message", function (msg) {
hubConnection.on("Message", msg => { expect(msg).toBe(message);
expect(msg).toBe(message); done();
resolve();
});
}); });
hubConnection.start() hubConnection.start().then(function () {
.then(() => { return hubConnection.invoke('InvokeWithString', message);
return Promise.all([hubConnection.invoke('InvokeWithString', message), callbackPromise]); })
}) .then(function() {
.then(() => { return hubConnection.stop();
return stop(); })
}) .catch(function (e) {
.then(() => { fail(e);
done(); done();
}) });
.catch(e => {
fail(e);
done();
});
}); });
it(`closed with error if hub cannot be created`, done => { it('closed with error if hub cannot be created', function (done) {
let errorRegex = { var errorRegex = {
WebSockets: "1011", // Message is browser specific (e.g. 'Websocket closed with status code: 1011') 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", LongPolling: "Internal Server Error",
ServerSentEvents: "Error occurred" ServerSentEvents: "Error occurred"
}; };
let logger = new signalR.ConsoleLogger(signalR.LogLevel.Information); var logger = new signalR.ConsoleLogger(signalR.LogLevel.Information);
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection( var hubConnection = new signalR.HubConnection(new signalR.HttpConnection('http://' + document.location.host + '/uncreatable', { transport: transportType, logger: logger }), logger, protocol);
`http://${document.location.host}/uncreatable`,
{ transport: transportType, logger: logger }),
logger,
protocol);
hubConnection.onClosed = error => { hubConnection.onClosed = function (error) {
expect(error.message).toMatch(errorRegex[signalR.TransportType[transportType]]); expect(error.message).toMatch(errorRegex[signalR.TransportType[transportType]]);
done(); done();
} };
hubConnection.start(); hubConnection.start();
}); });
}); });

View File

@ -1,22 +1,24 @@
'use strict';
describe('WebSockets', function () { describe('WebSockets', function () {
it('can be used to connect to SignalR', done => { it('can be used to connect to SignalR', function (done) {
const message = "message"; var message = "message";
let webSocket = new WebSocket(ECHOENDPOINT_URL.replace(/^http/, "ws")); var webSocket = new WebSocket(ECHOENDPOINT_URL.replace(/^http/, "ws"));
webSocket.onopen = () => { webSocket.onopen = function () {
webSocket.send(message); webSocket.send(message);
}; };
var received = ""; var received = "";
webSocket.onmessage = event => { webSocket.onmessage = function (event) {
received += event.data; received += event.data;
if (received === message) { if (received === message) {
webSocket.close(); webSocket.close();
} }
}; };
webSocket.onclose = event => { webSocket.onclose = function (event) {
if (!event.wasClean) { if (!event.wasClean) {
fail("connection closed with unexpected status code: " + event.code + " " + event.reason); fail("connection closed with unexpected status code: " + event.code + " " + event.reason);
} }