Integrating MsgPack support in TS client

This commit is contained in:
Pawel Kadluczka 2017-08-07 14:41:35 -07:00 committed by Pawel Kadluczka
parent 9eabce1b02
commit 4898c0d3df
15 changed files with 230 additions and 81 deletions

View File

@ -2,7 +2,7 @@ import { IHttpClient } from "../Microsoft.AspNetCore.SignalR.Client.TS/HttpClien
import { HttpConnection } from "../Microsoft.AspNetCore.SignalR.Client.TS/HttpConnection"
import { IHttpConnectionOptions } from "../Microsoft.AspNetCore.SignalR.Client.TS/IHttpConnectionOptions"
import { DataReceived, TransportClosed } from "../Microsoft.AspNetCore.SignalR.Client.TS/Common"
import { ITransport, TransportType } from "../Microsoft.AspNetCore.SignalR.Client.TS/Transports"
import { ITransport, TransportType, TransferMode } from "../Microsoft.AspNetCore.SignalR.Client.TS/Transports"
import { eachTransport } from "./Common";
describe("Connection", () => {
@ -112,7 +112,7 @@ describe("Connection", () => {
}
} as IHttpConnectionOptions;
var connection = new HttpConnection("http://tempuri.org", options);
let connection = new HttpConnection("http://tempuri.org", options);
try {
await connection.start();
@ -125,7 +125,7 @@ describe("Connection", () => {
});
it("can stop a non-started connection", async (done) => {
var connection = new HttpConnection("http://tempuri.org");
let connection = new HttpConnection("http://tempuri.org");
await connection.stop();
done();
});
@ -133,16 +133,16 @@ describe("Connection", () => {
it("preserves users connection string", async done => {
let connectUrl: string;
let fakeTransport: ITransport = {
connect(url: string): Promise<void> {
connect(url: string): Promise<TransferMode> {
connectUrl = url;
return Promise.reject("");
return Promise.reject(TransferMode.Text);
},
send(data: any): Promise<void> {
return Promise.reject("");
},
stop(): void { },
onDataReceived: undefined,
onClosed: undefined
onClosed: undefined,
}
let options: IHttpConnectionOptions = {
@ -158,7 +158,7 @@ describe("Connection", () => {
} as IHttpConnectionOptions;
var connection = new HttpConnection("http://tempuri.org?q=myData", options);
let connection = new HttpConnection("http://tempuri.org?q=myData", options);
try {
await connection.start();
@ -173,7 +173,7 @@ describe("Connection", () => {
});
eachTransport((requestedTransport: TransportType) => {
it(`Connection cannot be started if requested ${TransportType[requestedTransport]} transport not available on server`, async done => {
it(`cannot be started if requested ${TransportType[requestedTransport]} transport not available on server`, async done => {
let options: IHttpConnectionOptions = {
httpClient: <IHttpClient>{
options(url: string): Promise<string> {
@ -186,7 +186,7 @@ describe("Connection", () => {
transport: requestedTransport
} as IHttpConnectionOptions;
var connection = new HttpConnection("http://tempuri.org", options);
let connection = new HttpConnection("http://tempuri.org", options);
try {
await connection.start();
fail();
@ -199,7 +199,7 @@ describe("Connection", () => {
});
});
it(`Connection cannot be started if no transport available on server and no transport requested`, async done => {
it("cannot be started if no transport available on server and no transport requested", async done => {
let options: IHttpConnectionOptions = {
httpClient: <IHttpClient>{
options(url: string): Promise<string> {
@ -211,7 +211,7 @@ describe("Connection", () => {
}
} as IHttpConnectionOptions;
var connection = new HttpConnection("http://tempuri.org", options);
let connection = new HttpConnection("http://tempuri.org", options);
try {
await connection.start();
fail();
@ -222,4 +222,42 @@ describe("Connection", () => {
done();
}
});
[
[TransferMode.Text, TransferMode.Text],
[TransferMode.Text, TransferMode.Binary],
[TransferMode.Binary, TransferMode.Text],
[TransferMode.Binary, TransferMode.Binary],
].forEach(([requestedTransferMode, transportTransferMode]) => {
it(`connection returns ${transportTransferMode} transfer mode when ${requestedTransferMode} transfer mode is requested`, async () => {
let fakeTransport = {
// mode: TransferMode : TransferMode.Text
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> { return Promise.resolve(transportTransferMode); },
send(data: any): Promise<void> { return Promise.resolve(); },
stop(): void {},
onDataReceived: null,
onClosed: null,
mode: transportTransferMode
} as ITransport;
let options: IHttpConnectionOptions = {
httpClient: <IHttpClient>{
options(url: string): Promise<string> {
return Promise.resolve("{ \"connectionId\": \"42\", \"availableTransports\": [] }");
},
get(url: string): Promise<string> {
return Promise.resolve("");
}
},
transport: fakeTransport
} as IHttpConnectionOptions;
let connection = new HttpConnection("https://tempuri.org", options);
connection.features.transferMode = requestedTransferMode;
await connection.start();
let actualTransferMode = connection.features.transferMode;
expect(actualTransferMode).toBe(transportTransferMode);
});
});
});

View File

@ -1,7 +1,7 @@
import { IConnection } from "../Microsoft.AspNetCore.SignalR.Client.TS/IConnection"
import { HubConnection } from "../Microsoft.AspNetCore.SignalR.Client.TS/HubConnection"
import { DataReceived, ConnectionClosed } from "../Microsoft.AspNetCore.SignalR.Client.TS/Common"
import { TransportType, ITransport } from "../Microsoft.AspNetCore.SignalR.Client.TS/Transports"
import { TransportType, ITransport, TransferMode } from "../Microsoft.AspNetCore.SignalR.Client.TS/Transports"
import { Observer } from "../Microsoft.AspNetCore.SignalR.Client.TS/Observable"
import { TextMessageFormat } from "../Microsoft.AspNetCore.SignalR.Client.TS/Formatters"
@ -26,7 +26,7 @@ describe("HubConnection", () => {
let connection = new TestConnection();
let hubConnection = new HubConnection(connection);
var invokePromise = hubConnection.send("testMethod", "arg", 42)
let invokePromise = hubConnection.send("testMethod", "arg", 42)
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
// Verify the message is sent
@ -52,7 +52,7 @@ describe("HubConnection", () => {
let connection = new TestConnection();
let hubConnection = new HubConnection(connection);
var invokePromise = hubConnection.invoke("testMethod", "arg", 42)
let invokePromise = hubConnection.invoke("testMethod", "arg", 42)
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
// Verify the message is sent
@ -76,7 +76,7 @@ describe("HubConnection", () => {
let connection = new TestConnection();
let hubConnection = new HubConnection(connection);
var invokePromise = hubConnection.invoke("testMethod", "arg", 42);
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
connection.receive({ type: 3, invocationId: connection.lastInvocationId, error: "foo" });
@ -88,7 +88,7 @@ describe("HubConnection", () => {
let connection = new TestConnection();
let hubConnection = new HubConnection(connection);
var invokePromise = hubConnection.invoke("testMethod", "arg", 42);
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
connection.receive({ type: 3, invocationId: connection.lastInvocationId, result: "foo" });
@ -99,7 +99,7 @@ describe("HubConnection", () => {
let connection = new TestConnection();
let hubConnection = new HubConnection(connection);
var invokePromise = hubConnection.invoke("testMethod");
let invokePromise = hubConnection.invoke("testMethod");
hubConnection.stop();
let ex = await captureException(async () => await invokePromise);
@ -110,7 +110,7 @@ describe("HubConnection", () => {
let connection = new TestConnection();
let hubConnection = new HubConnection(connection);
var invokePromise = hubConnection.invoke("testMethod");
let invokePromise = hubConnection.invoke("testMethod");
// Typically this would be called by the transport
connection.onClosed(new Error("Connection lost"));
@ -137,7 +137,7 @@ describe("HubConnection", () => {
let connection = new TestConnection();
let hubConnection = new HubConnection(connection);
var invokePromise = hubConnection.stream("testStream", "arg", 42);
let invokePromise = hubConnection.stream("testStream", "arg", 42);
// Verify the message is sent
expect(connection.sentData.length).toBe(1);
@ -168,7 +168,6 @@ describe("HubConnection", () => {
let ex = await captureException(async () => await observer.completed);
expect(ex.message).toEqual("Error: foo");
});
it("completes the observer when a completion is received", async () => {
@ -250,12 +249,14 @@ describe("HubConnection", () => {
});
class TestConnection implements IConnection {
readonly features: any = {};
start(): Promise<void> {
return Promise.resolve();
};
send(data: any): Promise<void> {
var invocation = TextMessageFormat.parse(data)[0];
let invocation = TextMessageFormat.parse(data)[0];
this.lastInvocationId = JSON.parse(invocation).invocationId;
if (this.sentData) {
this.sentData.push(invocation);
@ -273,7 +274,7 @@ class TestConnection implements IConnection {
};
receive(data: any): void {
var payload = JSON.stringify(data);
let payload = JSON.stringify(data);
this.onDataReceived(TextMessageFormat.write(payload));
}

View File

@ -1,10 +1,10 @@
import { DataReceived, ConnectionClosed } from "./Common"
import { IConnection } from "./IConnection"
import { ITransport, TransportType, WebSocketTransport, ServerSentEventsTransport, LongPollingTransport } from "./Transports"
import { ITransport, TransferMode, TransportType, WebSocketTransport, ServerSentEventsTransport, LongPollingTransport } from "./Transports"
import { IHttpClient, HttpClient } from "./HttpClient"
import { IHttpConnectionOptions } from "./IHttpConnectionOptions"
enum ConnectionState {
const enum ConnectionState {
Initial,
Connecting,
Connected,
@ -25,6 +25,8 @@ export class HttpConnection implements IConnection {
private options: IHttpConnectionOptions;
private startPromise: Promise<void>;
readonly features: any = {};
constructor(url: string, options: IHttpConnectionOptions = {}) {
this.url = url;
this.httpClient = options.httpClient || new HttpClient();
@ -59,7 +61,14 @@ export class HttpConnection implements IConnection {
this.transport = this.createTransport(this.options.transport, negotiateResponse.availableTransports);
this.transport.onDataReceived = this.onDataReceived;
this.transport.onClosed = e => this.stopConnection(true, e);
await this.transport.connect(this.url);
let requestedTransferMode =
this.features.transferMode === TransferMode.Binary
? TransferMode.Binary
: TransferMode.Text;
this.features.transferMode = await this.transport.connect(this.url, requestedTransferMode);
// 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);

View File

@ -1,13 +1,15 @@
import { ConnectionClosed } from "./Common"
import { IConnection } from "./IConnection"
import { TransportType } from "./Transports"
import { TransportType, TransferMode } from "./Transports"
import { Subject, Observable } from "./Observable"
export { TransportType } from "./Transports"
export { HttpConnection } from "./HttpConnection"
import { IHubProtocol, MessageType, HubMessage, CompletionMessage, ResultMessage, InvocationMessage, NegotiationMessage } from "./IHubProtocol";
import { IHubProtocol, ProtocolType, MessageType, HubMessage, CompletionMessage, ResultMessage, InvocationMessage, NegotiationMessage } from "./IHubProtocol";
import { JsonHubProtocol } from "./JsonHubProtocol";
import { TextMessageFormat } from "./Formatters"
export { TransportType } from "./Transports"
export { HttpConnection } from "./HttpConnection"
export { JsonHubProtocol } from "./JsonHubProtocol"
export class HubConnection {
private connection: IConnection;
private callbacks: Map<string, (invocationUpdate: CompletionMessage | ResultMessage) => void>;
@ -91,10 +93,22 @@ export class HubConnection {
}
async start(): Promise<void> {
let requestedTransferMode =
(this.protocol.type === ProtocolType.Binary)
? TransferMode.Binary
: TransferMode.Text;
this.connection.features.transferMode = requestedTransferMode
await this.connection.start();
var actualTransferMode = this.connection.features.transferMode;
await this.connection.send(
TextMessageFormat.write(
JSON.stringify(<NegotiationMessage>{ protocol: this.protocol.name()})));
JSON.stringify(<NegotiationMessage>{ protocol: this.protocol.name})));
if (requestedTransferMode === TransferMode.Binary && actualTransferMode === TransferMode.Text) {
this.protocol = new Base64EncodedHubProtocol(this.protocol);
}
}
stop(): void {
@ -196,3 +210,38 @@ export class HubConnection {
};
}
}
class Base64EncodedHubProtocol implements IHubProtocol {
private wrappedProtocol: IHubProtocol;
constructor(protocol: IHubProtocol) {
this.wrappedProtocol = protocol;
this.name = this.wrappedProtocol.name;
this.type = ProtocolType.Text;
}
readonly name: string;
readonly type: ProtocolType;
parseMessages(input: any): HubMessage[] {
// atob/btoa are browsers APIs but they can be polyfilled. If this becomes problematic we can use
// base64-js module
let s = atob(input);
let 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) {
let payload = new Uint8Array(this.wrappedProtocol.writeMessage(message));
let s = "";
for (var 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
return btoa(s);
}
}

View File

@ -1,7 +1,9 @@
import { DataReceived, ConnectionClosed } from "./Common"
import { TransportType, ITransport } from "./Transports"
import { TransportType, TransferMode, ITransport } from "./Transports"
export interface IConnection {
readonly features: any;
start(): Promise<void>;
send(data: any): Promise<void>;
stop(): void;

View File

@ -28,8 +28,14 @@ export interface NegotiationMessage {
readonly protocol: string;
}
export const enum ProtocolType {
Text = 1,
Binary
}
export interface IHubProtocol {
name(): string;
readonly name: string;
readonly type: ProtocolType;
parseMessages(input: any): HubMessage[];
writeMessage(message: HubMessage): any;
}

View File

@ -1,10 +1,11 @@
import { TextMessageFormat } from "./Formatters";
import { IHubProtocol, HubMessage } from "./IHubProtocol";
import { IHubProtocol, ProtocolType, HubMessage } from "./IHubProtocol";
export class JsonHubProtocol implements IHubProtocol {
name(): string {
return "json"
}
readonly name: string = "json";
readonly type: ProtocolType = ProtocolType.Text;
parseMessages(input: string): HubMessage[] {
if (!input) {

View File

@ -1,11 +1,12 @@
import { IHubProtocol, MessageType, HubMessage, InvocationMessage, ResultMessage, CompletionMessage } from "./IHubProtocol";
import { IHubProtocol, ProtocolType, MessageType, HubMessage, InvocationMessage, ResultMessage, CompletionMessage } from "./IHubProtocol";
import { BinaryMessageFormat } from "./Formatters"
import * as msgpack5 from "msgpack5"
export class MessagePackHubProtocol implements IHubProtocol {
name(): string {
return "messagepack";
}
readonly name: string = "messagepack";
readonly type: ProtocolType = ProtocolType.Binary;
parseMessages(input: ArrayBuffer): HubMessage[] {
return BinaryMessageFormat.parse(input).map(m => this.parseMessage(m));

View File

@ -1,5 +1,6 @@
import { DataReceived, TransportClosed } from "./Common"
import { IHttpClient } from "./HttpClient"
import { HttpError } from "./HttpError"
export enum TransportType {
WebSockets,
@ -7,8 +8,13 @@ export enum TransportType {
LongPolling
}
export const enum TransferMode {
Text = 1,
Binary
}
export interface ITransport {
connect(url: string): Promise<void>;
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode>;
send(data: any): Promise<void>;
stop(): void;
onDataReceived: DataReceived;
@ -18,16 +24,20 @@ export interface ITransport {
export class WebSocketTransport implements ITransport {
private webSocket: WebSocket;
connect(url: string, queryString: string = ""): Promise<void> {
return new Promise<void>((resolve, reject) => {
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> {
return new Promise<TransferMode>((resolve, reject) => {
url = url.replace(/^http/, "ws");
let webSocket = new WebSocket(url);
if (requestedTransferMode == TransferMode.Binary) {
webSocket.binaryType = "arraybuffer";
}
webSocket.onopen = (event: Event) => {
console.log(`WebSocket connected to ${url}`);
this.webSocket = webSocket;
resolve();
resolve(requestedTransferMode);
};
webSocket.onerror = (event: Event) => {
@ -78,20 +88,19 @@ export class WebSocketTransport implements ITransport {
export class ServerSentEventsTransport implements ITransport {
private eventSource: EventSource;
private url: string;
private queryString: string;
private httpClient: IHttpClient;
constructor(httpClient: IHttpClient) {
this.httpClient = httpClient;
}
connect(url: string): Promise<void> {
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> {
if (typeof (EventSource) === "undefined") {
Promise.reject("EventSource not supported by the browser.")
Promise.reject("EventSource not supported by the browser.");
}
this.url = url;
return new Promise<void>((resolve, reject) => {
return new Promise<TransferMode>((resolve, reject) => {
let eventSource = new EventSource(this.url);
try {
@ -121,7 +130,8 @@ export class ServerSentEventsTransport implements ITransport {
eventSource.onopen = () => {
console.log(`SSE connected to ${this.url}`);
this.eventSource = eventSource;
resolve();
// SSE is a text protocol
resolve(TransferMode.Text);
}
}
catch (e) {
@ -155,19 +165,22 @@ export class LongPollingTransport implements ITransport {
this.httpClient = httpClient;
}
connect(url: string): Promise<void> {
connect(url: string, requestedTransferMode: TransferMode): Promise<TransferMode> {
this.url = url;
this.shouldPoll = true;
this.poll(this.url);
return Promise.resolve();
this.poll(this.url, requestedTransferMode);
return Promise.resolve(requestedTransferMode);
}
private poll(url: string): void {
private poll(url: string, transferMode: TransferMode): void {
if (!this.shouldPoll) {
return;
}
let pollXhr = new XMLHttpRequest();
if (transferMode === TransferMode.Binary) {
pollXhr.responseType = "arraybuffer";
}
pollXhr.onload = () => {
if (pollXhr.status == 200) {
@ -187,7 +200,7 @@ export class LongPollingTransport implements ITransport {
return;
}
}
this.poll(url);
this.poll(url, transferMode);
}
else if (this.pollXhr.status == 204) {
if (this.onClosed) {
@ -196,7 +209,7 @@ export class LongPollingTransport implements ITransport {
}
else {
if (this.onClosed) {
this.onClosed(new Error(`Status: ${pollXhr.status}, Message: ${pollXhr.responseText}`));
this.onClosed(new HttpError(pollXhr.statusText, pollXhr.status));
}
}
};
@ -209,7 +222,7 @@ export class LongPollingTransport implements ITransport {
};
pollXhr.ontimeout = () => {
this.poll(url);
this.poll(url, transferMode);
}
this.pollXhr = pollXhr;

View File

@ -24,7 +24,15 @@ gulp.task('browserify-client', ['compile-ts-client'], () => {
.pipe(gulp.dest(clientOutDir + '/../browser'));
});
gulp.task('browserify-msgpackprotocol', ['compile-ts-client'], () => {
return browserify(clientOutDir + '/MessagePackHubProtocol.js', {standalone: 'signalRMsgPack'})
.bundle()
.pipe(source('signalr-msgpackprotocol.js'))
.pipe(gulp.dest(clientOutDir + '/../browser'));
});
gulp.task('build-ts-client', ['clean', 'compile-ts-client', 'browserify-client']);
gulp.task('browserify', [ 'browserify-client', 'browserify-msgpackprotocol']);
gulp.task('build-ts-client', ['clean', 'compile-ts-client', 'browserify']);
gulp.task('default', ['build-ts-client']);

View File

@ -26,7 +26,8 @@
</ItemGroup>
<Copy SourceFiles="@(JasmineFiles)" DestinationFolder="$(MSBuildProjectDirectory)/wwwroot/lib/jasmine" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)..\dist\browser\signalr-client.js" DestinationFolder="$(MSBuildThisFileDirectory)wwwroot\lib\signalr-client" />
<Copy SourceFiles="$(MSBuildThisFileDirectory)..\dist\browser\signalr-client.js;$(MSBuildThisFileDirectory)..\dist\browser\signalr-msgpackprotocol.js"
DestinationFolder="$(MSBuildThisFileDirectory)wwwroot\lib\signalr" />
</Target>
</Project>

View File

@ -8,7 +8,8 @@
<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/boot.js"></script>
<script type="text/javascript" src="lib/signalr-client/signalr-client.js"></script>
<script type="text/javascript" src="lib/signalr/signalr-client.js"></script>
<script type="text/javascript" src="lib/signalr/signalr-msgpackprotocol.js"></script>
<script src="js/common.js"></script>
<script src="js/webSocketTests.js"></script>
<script src="js/connectionTests.js"></script>

View File

@ -5,5 +5,18 @@ function eachTransport(action) {
signalR.TransportType.WebSockets,
signalR.TransportType.ServerSentEvents,
signalR.TransportType.LongPolling ];
transportTypes.forEach(t => action(t));
transportTypes.forEach(t => action(t));
}
function eachTransportAndProtocol(action) {
let transportTypes = [
signalR.TransportType.WebSockets,
signalR.TransportType.ServerSentEvents,
signalR.TransportType.LongPolling ];
let protocols = [
new signalR.JsonHubProtocol(),
new signalRMsgPack.MessagePackHubProtocol()
];
transportTypes.forEach(t =>
protocols.forEach(p => action(t, p)));
}

View File

@ -1,11 +1,12 @@
const TESTHUBENDPOINT_URL = `http://${document.location.host}/testhub`;
describe('hubConnection', () => {
eachTransport(transportType => {
describe(`${signalR.TransportType[transportType]} transport`, () => {
eachTransportAndProtocol((transportType, protocol) => {
describe(`${protocol.name} over ${signalR.TransportType[transportType]} transport`, () => {
it(`can invoke server method and receive result`, done => {
const message = "Hi";
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }));
let hubConnection = new signalR.HubConnection(
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol);
hubConnection.onClosed = error => {
expect(error).toBe(undefined);
done();
@ -31,7 +32,8 @@ describe('hubConnection', () => {
});
it(`can stream server method and receive result`, done => {
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }));
let hubConnection = new signalR.HubConnection(
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol);
hubConnection.onClosed = error => {
expect(error).toBe(undefined);
done();
@ -63,7 +65,8 @@ describe('hubConnection', () => {
it(`rethrows an exception from the server when invoking`, done => {
const errorMessage = "An error occurred.";
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }));
let hubConnection = new signalR.HubConnection(
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol);
hubConnection.start()
.then(() => {
@ -90,7 +93,8 @@ describe('hubConnection', () => {
it(`rethrows an exception from the server when streaming`, done => {
const errorMessage = "An error occurred.";
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }));
let hubConnection = new signalR.HubConnection(
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol);
hubConnection.start()
.then(() => {
@ -116,7 +120,8 @@ describe('hubConnection', () => {
});
it(`can receive server calls`, done => {
let client = new signalR.HubConnection(new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }));
let client = new signalR.HubConnection(
new signalR.HttpConnection(TESTHUBENDPOINT_URL, { transport: transportType }), protocol);
const message = "Hello SignalR";
let callbackPromise = new Promise((resolve, reject) => {
@ -141,22 +146,23 @@ describe('hubConnection', () => {
done();
});
});
});
it(`over ${signalR.TransportType[transportType]} closed with error if hub cannot be created`, done =>{
let errorRegex = {
WebSockets: "1011", // Message is browser specific (e.g. 'Websocket closed with status code: 1011')
LongPolling: "Status: 500",
ServerSentEvents: "Error occurred"
};
it(`closed with error if hub cannot be created`, done => {
let errorRegex = {
WebSockets: "1011", // Message is browser specific (e.g. 'Websocket closed with status code: 1011')
LongPolling: "Internal Server Error",
ServerSentEvents: "Error occurred"
};
let hubConnection = new signalR.HubConnection(new signalR.HttpConnection(`http://${document.location.host}/uncreatable`, { transport: transportType }));
let hubConnection = new signalR.HubConnection(
new signalR.HttpConnection(`http://${document.location.host}/uncreatable`, { transport: transportType }), protocol);
hubConnection.onClosed = error => {
expect(error).toMatch(errorRegex[signalR.TransportType[transportType]]);
done();
}
hubConnection.start();
hubConnection.onClosed = error => {
expect(error.message).toMatch(errorRegex[signalR.TransportType[transportType]]);
done();
}
hubConnection.start();
});
});
});
});

View File

@ -171,7 +171,7 @@ namespace Microsoft.AspNetCore.SignalR.Internal.Protocol
completionMessage.HasResult ? NonVoidResult :
VoidResult;
packer.PackArrayHeader(3 + resultKind != VoidResult ? 1 : 0);
packer.PackArrayHeader(3 + (resultKind != VoidResult ? 1 : 0));
packer.Pack(CompletionMessageType);
packer.PackString(completionMessage.InvocationId);
packer.Pack(resultKind);