diff --git a/clients/ts/signalr/spec/HttpConnection.spec.ts b/clients/ts/signalr/spec/HttpConnection.spec.ts index 3c5f7d4771..8189c97e89 100644 --- a/clients/ts/signalr/spec/HttpConnection.spec.ts +++ b/clients/ts/signalr/spec/HttpConnection.spec.ts @@ -254,6 +254,66 @@ describe("HttpConnection", () => { expect(e.message).toBe("Unable to initialize any of the available transports."); } }); + + for (const [val, name] of [[null, "null"], [undefined, "undefined"], [0, "0"]]) { + it(`can be started using ${HttpTransportType[requestedTransport]} transport when transport mask is ${name}`, async () => { + const negotiateResponse = { + availableTransports: [ + { transport: "WebSockets", transferFormats: [ "Text", "Binary" ] }, + { transport: "ServerSentEvents", transferFormats: [ "Text" ] }, + { transport: "LongPolling", transferFormats: [ "Text", "Binary" ] }, + ], + connectionId: "abc123", + }; + + const options: IHttpConnectionOptions = { + ...commonOptions, + httpClient: new TestHttpClient() + .on("POST", (r) => negotiateResponse) + .on("GET", (r) => new HttpResponse(204)), + transport: val, + } as IHttpConnectionOptions; + + const connection = new HttpConnection("http://tempuri.org", options); + + await connection.start(TransferFormat.Text); + }); + } + + it(`cannot be started if server's only transport (${HttpTransportType[requestedTransport]}) is masked out by the transport option`, async() => { + const negotiateResponse = { + availableTransports: [ + { transport: "WebSockets", transferFormats: [ "Text", "Binary" ] }, + { transport: "ServerSentEvents", transferFormats: [ "Text" ] }, + { transport: "LongPolling", transferFormats: [ "Text", "Binary" ] }, + ], + connectionId: "abc123", + }; + + // Build the mask by inverting the requested transport + const transportMask = ~requestedTransport; + + // Remove all transports other than the requested one + negotiateResponse.availableTransports = negotiateResponse.availableTransports + .filter((r) => r.transport === HttpTransportType[requestedTransport]); + + const options: IHttpConnectionOptions = { + ...commonOptions, + httpClient: new TestHttpClient() + .on("POST", (r) => negotiateResponse) + .on("GET", (r) => new HttpResponse(204)), + transport: transportMask, + } as IHttpConnectionOptions; + + const connection = new HttpConnection("http://tempuri.org", options); + + try { + await connection.start(TransferFormat.Text); + fail("Expected connection.start to throw!"); + } catch (e) { + expect(e.message).toBe("Unable to initialize any of the available transports."); + } + }); }); it("cannot be started if no transport available on server and no transport requested", async (done) => { diff --git a/clients/ts/signalr/src/HttpConnection.ts b/clients/ts/signalr/src/HttpConnection.ts index 615cb07f70..558b936759 100644 --- a/clients/ts/signalr/src/HttpConnection.ts +++ b/clients/ts/signalr/src/HttpConnection.ts @@ -262,7 +262,7 @@ export class HttpConnection implements IConnection { this.logger.log(LogLevel.Debug, `Skipping transport '${endpoint.transport}' because it is not supported by this client.`); } else { const transferFormats = endpoint.transferFormats.map((s) => TransferFormat[s]); - if (!requestedTransport || transport === requestedTransport) { + if (transportMatches(requestedTransport, transport)) { if (transferFormats.indexOf(requestedTransferFormat) >= 0) { if ((transport === HttpTransportType.WebSockets && typeof WebSocket === "undefined") || (transport === HttpTransportType.ServerSentEvents && typeof EventSource === "undefined")) { @@ -282,7 +282,7 @@ export class HttpConnection implements IConnection { } private isITransport(transport: any): transport is ITransport { - return typeof (transport) === "object" && "connect" in transport; + return transport && typeof (transport) === "object" && "connect" in transport; } private changeState(from: ConnectionState, to: ConnectionState): boolean { @@ -345,3 +345,7 @@ export class HttpConnection implements IConnection { return negotiateUrl; } } + +function transportMatches(requestedTransport: HttpTransportType, actualTransport: HttpTransportType) { + return !requestedTransport || ((actualTransport & requestedTransport) !== 0); +}