parent
c7ebae47ea
commit
7317762b29
|
|
@ -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 { HttpTransportType, IHttpConnectionOptions, LogLevel, TransferFormat } from "@aspnet/signalr";
|
||||
import { HttpTransportType, IHttpConnectionOptions, TransferFormat } from "@aspnet/signalr";
|
||||
import { eachTransport, ECHOENDPOINT_URL } from "./Common";
|
||||
import { TestLogger } from "./TestLogger";
|
||||
|
||||
|
|
@ -23,9 +23,7 @@ describe("connection", () => {
|
|||
...commonOptions,
|
||||
});
|
||||
|
||||
let received = "";
|
||||
connection.onreceive = (data) => {
|
||||
received += data;
|
||||
if (data === message) {
|
||||
connection.stop();
|
||||
}
|
||||
|
|
@ -55,9 +53,7 @@ describe("connection", () => {
|
|||
transport: transportType,
|
||||
});
|
||||
|
||||
let received = "";
|
||||
connection.onreceive = (data) => {
|
||||
received += data;
|
||||
if (data === message) {
|
||||
connection.stop();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 { AbortError, DefaultHttpClient, HttpClient, HttpRequest, HttpResponse, HttpTransportType, HubConnection, HubConnectionBuilder, IHttpConnectionOptions, IStreamSubscriber, JsonHubProtocol, LogLevel } from "@aspnet/signalr";
|
||||
import { AbortError, DefaultHttpClient, HttpClient, HttpRequest, HttpResponse, HttpTransportType, HubConnectionBuilder, IHttpConnectionOptions, JsonHubProtocol } from "@aspnet/signalr";
|
||||
import { MessagePackHubProtocol } from "@aspnet/signalr-protocol-msgpack";
|
||||
|
||||
import { eachTransport, eachTransportAndProtocol, ENDPOINT_BASE_URL } from "./Common";
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "cd ./signalr && npm run build && cd ../signalr-protocol-msgpack && npm run build",
|
||||
"pretest": "tslint -c ./tslint.json -p ./signalr/tests/tsconfig.json && tslint -c ./tslint.json -p ./signalr-protocol-msgpack/tests/tsconfig.json",
|
||||
"test": "jest",
|
||||
"coverage": "jest --coverage"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import { IHttpConnectionOptions } from "./IHttpConnectionOptions";
|
|||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { HttpTransportType, ITransport, TransferFormat } from "./ITransport";
|
||||
import { LongPollingTransport } from "./LongPollingTransport";
|
||||
import { EventSourceConstructor, WebSocketConstructor } from "./Polyfills";
|
||||
import { ServerSentEventsTransport } from "./ServerSentEventsTransport";
|
||||
import { Arg, createLogger } from "./Utils";
|
||||
import { WebSocketTransport } from "./WebSocketTransport";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import { IConnection } from "./IConnection";
|
|||
import { CancelInvocationMessage, CompletionMessage, IHubProtocol, InvocationMessage, MessageType, StreamInvocationMessage, StreamItemMessage } from "./IHubProtocol";
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { IStreamResult } from "./Stream";
|
||||
import { TextMessageFormat } from "./TextMessageFormat";
|
||||
import { Arg, Subject } from "./Utils";
|
||||
|
||||
const DEFAULT_TIMEOUT_IN_MS: number = 30 * 1000;
|
||||
|
|
@ -354,7 +353,11 @@ export class HubConnection {
|
|||
break;
|
||||
case MessageType.Close:
|
||||
this.logger.log(LogLevel.Information, "Close message received from server.");
|
||||
|
||||
// We don't want to wait on the stop itself.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
this.connection.stop(message.error ? new Error("Server returned an error on close: " + message.error) : undefined);
|
||||
|
||||
break;
|
||||
default:
|
||||
this.logger.log(LogLevel.Warning, "Invalid message type: " + message.type);
|
||||
|
|
@ -377,12 +380,18 @@ export class HubConnection {
|
|||
this.logger.log(LogLevel.Error, message);
|
||||
|
||||
const error = new Error(message);
|
||||
|
||||
// We don't want to wait on the stop itself.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
this.connection.stop(error);
|
||||
throw error;
|
||||
}
|
||||
if (responseMessage.error) {
|
||||
const message = "Server returned handshake error: " + responseMessage.error;
|
||||
this.logger.log(LogLevel.Error, message);
|
||||
|
||||
// We don't want to wait on the stop itself.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
this.connection.stop(new Error(message));
|
||||
} else {
|
||||
this.logger.log(LogLevel.Debug, "Server handshake complete.");
|
||||
|
|
@ -405,7 +414,8 @@ export class HubConnection {
|
|||
|
||||
private serverTimeout() {
|
||||
// The server hasn't talked to us in a while. It doesn't like us anymore ... :(
|
||||
// Terminate the connection
|
||||
// Terminate the connection, but we don't need to wait on the promise.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
this.connection.stop(new Error("Server timeout elapsed without receiving a message from the server."));
|
||||
}
|
||||
|
||||
|
|
@ -417,6 +427,9 @@ export class HubConnection {
|
|||
// This is not supported in v1. So we return an error to avoid blocking the server waiting for the response.
|
||||
const message = "Server requested a response, which is not supported in this version of the client.";
|
||||
this.logger.log(LogLevel.Error, message);
|
||||
|
||||
// We don't need to wait on this Promise.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
this.connection.stop(new Error(message));
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ export class LongPollingTransport implements ITransport {
|
|||
private readonly pollAbort: AbortController;
|
||||
|
||||
private url?: string;
|
||||
private pollXhr?: XMLHttpRequest;
|
||||
private running: boolean;
|
||||
private receiving?: Promise<void>;
|
||||
private closeError?: Error;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ export interface IStreamResult<T> {
|
|||
*
|
||||
* @typeparam T The type of the items being sent by the server.
|
||||
*/
|
||||
// We can't remove this, it's a breaking change, but it's not used.
|
||||
// tslint:disable-next-line:no-unused-variable
|
||||
export interface ISubscription<T> {
|
||||
/** Disconnects the {@link IStreamSubscriber} associated with this subscription from the stream. */
|
||||
dispose(): void;
|
||||
|
|
|
|||
|
|
@ -7,15 +7,15 @@ import { IHttpConnectionOptions } from "../src/IHttpConnectionOptions";
|
|||
import { HttpTransportType, ITransport, TransferFormat } from "../src/ITransport";
|
||||
|
||||
import { HttpError } from "../src/Errors";
|
||||
import { LogLevel } from "../src/ILogger";
|
||||
import { EventSourceConstructor, WebSocketConstructor } from "../src/Polyfills";
|
||||
import { TextMessageFormat } from "../src/TextMessageFormat";
|
||||
import { WebSocketTransport } from "../src/WebSocketTransport";
|
||||
import { NullLogger } from "../src/Loggers";
|
||||
import { EventSourceConstructor } from "../src/Polyfills";
|
||||
|
||||
import { eachEndpointUrl, eachTransport } from "./Common";
|
||||
import { TestHttpClient } from "./TestHttpClient";
|
||||
import { PromiseSource } from "./Utils";
|
||||
|
||||
const commonOptions: IHttpConnectionOptions = {
|
||||
logger: NullLogger.instance,
|
||||
};
|
||||
|
||||
const defaultConnectionId = "abc123";
|
||||
|
|
@ -45,13 +45,13 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => Promise.reject("error"))
|
||||
.on("GET", (r) => ""),
|
||||
.on("POST", () => Promise.reject("error"))
|
||||
.on("GET", () => ""),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
expect(connection.start(TransferFormat.Text))
|
||||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("error");
|
||||
});
|
||||
|
|
@ -61,15 +61,15 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => {
|
||||
.on("POST", () => {
|
||||
negotiating.resolve();
|
||||
return defaultNegotiateResponse;
|
||||
}),
|
||||
transport: {
|
||||
connect(url: string, transferFormat: TransferFormat) {
|
||||
connect() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
send(data: any) {
|
||||
send() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
stop() {
|
||||
|
|
@ -93,15 +93,13 @@ describe("HttpConnection", () => {
|
|||
});
|
||||
|
||||
it("can start a stopped connection", async () => {
|
||||
let negotiateCalls = 0;
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => {
|
||||
negotiateCalls += 1;
|
||||
.on("POST", () => {
|
||||
return Promise.reject("reached negotiate");
|
||||
})
|
||||
.on("GET", (r) => ""),
|
||||
.on("GET", () => ""),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -119,12 +117,12 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => {
|
||||
connection.stop();
|
||||
.on("POST", async () => {
|
||||
await connection.stop();
|
||||
return "{}";
|
||||
})
|
||||
.on("GET", (r) => {
|
||||
connection.stop();
|
||||
.on("GET", async () => {
|
||||
await connection.stop();
|
||||
return "";
|
||||
}),
|
||||
} as IHttpConnectionOptions;
|
||||
|
|
@ -143,8 +141,8 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => ({ connectionId: "42", availableTransports: [] }))
|
||||
.on("GET", (r) => { throw new Error("fail"); }),
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [] }))
|
||||
.on("GET", () => { throw new Error("fail"); }),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org?q=myData", options);
|
||||
|
|
@ -161,7 +159,7 @@ describe("HttpConnection", () => {
|
|||
connectUrl.resolve(url);
|
||||
return Promise.resolve();
|
||||
},
|
||||
send(data: any): Promise<void> {
|
||||
send(): Promise<void> {
|
||||
return Promise.resolve();
|
||||
},
|
||||
stop(): Promise<void> {
|
||||
|
|
@ -174,8 +172,8 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => "{ \"connectionId\": \"42\" }")
|
||||
.on("GET", (r) => ""),
|
||||
.on("POST", () => "{ \"connectionId\": \"42\" }")
|
||||
.on("GET", () => ""),
|
||||
transport: fakeTransport,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -201,10 +199,10 @@ describe("HttpConnection", () => {
|
|||
negotiateUrl.resolve(r.url);
|
||||
throw new HttpError("We don't care how this turns out", 500);
|
||||
})
|
||||
.on("GET", (r) => {
|
||||
.on("GET", () => {
|
||||
return new HttpResponse(204);
|
||||
})
|
||||
.on("DELETE", (r) => new HttpResponse(202)),
|
||||
.on("DELETE", () => new HttpResponse(202)),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection(givenUrl, options);
|
||||
|
|
@ -232,8 +230,8 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => negotiateResponse)
|
||||
.on("GET", (r) => new HttpResponse(204)),
|
||||
.on("POST", () => negotiateResponse)
|
||||
.on("GET", () => new HttpResponse(204)),
|
||||
transport: requestedTransport,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -248,9 +246,9 @@ describe("HttpConnection", () => {
|
|||
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" ] },
|
||||
{ transport: "WebSockets", transferFormats: ["Text", "Binary"] },
|
||||
{ transport: "ServerSentEvents", transferFormats: ["Text"] },
|
||||
{ transport: "LongPolling", transferFormats: ["Text", "Binary"] },
|
||||
],
|
||||
connectionId: "abc123",
|
||||
};
|
||||
|
|
@ -258,8 +256,8 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => negotiateResponse)
|
||||
.on("GET", (r) => new HttpResponse(204)),
|
||||
.on("POST", () => negotiateResponse)
|
||||
.on("GET", () => new HttpResponse(204)),
|
||||
transport: val,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -272,9 +270,9 @@ describe("HttpConnection", () => {
|
|||
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" ] },
|
||||
{ transport: "WebSockets", transferFormats: ["Text", "Binary"] },
|
||||
{ transport: "ServerSentEvents", transferFormats: ["Text"] },
|
||||
{ transport: "LongPolling", transferFormats: ["Text", "Binary"] },
|
||||
],
|
||||
connectionId: "abc123",
|
||||
};
|
||||
|
|
@ -289,8 +287,8 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => negotiateResponse)
|
||||
.on("GET", (r) => new HttpResponse(204)),
|
||||
.on("POST", () => negotiateResponse)
|
||||
.on("GET", () => new HttpResponse(204)),
|
||||
transport: transportMask,
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
|
|
@ -309,8 +307,8 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => ({ connectionId: "42", availableTransports: [] }))
|
||||
.on("GET", (r) => ""),
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [] }))
|
||||
.on("GET", () => ""),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -351,7 +349,7 @@ describe("HttpConnection", () => {
|
|||
let firstNegotiate = true;
|
||||
let firstPoll = true;
|
||||
const httpClient = new TestHttpClient()
|
||||
.on("POST", /negotiate$/, (r) => {
|
||||
.on("POST", /negotiate$/, () => {
|
||||
if (firstNegotiate) {
|
||||
firstNegotiate = false;
|
||||
return { url: "https://another.domain.url/chat" };
|
||||
|
|
@ -361,14 +359,14 @@ describe("HttpConnection", () => {
|
|||
connectionId: "0rge0d00-0040-0030-0r00-000q00r00e00",
|
||||
};
|
||||
})
|
||||
.on("GET", (r) => {
|
||||
.on("GET", () => {
|
||||
if (firstPoll) {
|
||||
firstPoll = false;
|
||||
return "";
|
||||
}
|
||||
return new HttpResponse(204, "No Content", "");
|
||||
})
|
||||
.on("DELETE", (r) => new HttpResponse(202));
|
||||
.on("DELETE", () => new HttpResponse(202));
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
|
|
@ -392,7 +390,7 @@ describe("HttpConnection", () => {
|
|||
|
||||
it("fails to start if negotiate redirects more than 100 times", async () => {
|
||||
const httpClient = new TestHttpClient()
|
||||
.on("POST", /negotiate$/, (r) => ({ url: "https://another.domain.url/chat" }));
|
||||
.on("POST", /negotiate$/, () => ({ url: "https://another.domain.url/chat" }));
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
|
|
@ -441,7 +439,7 @@ describe("HttpConnection", () => {
|
|||
}
|
||||
return new HttpResponse(204, "No Content", "");
|
||||
})
|
||||
.on("DELETE", (r) => new HttpResponse(202));
|
||||
.on("DELETE", () => new HttpResponse(202));
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
|
|
@ -481,7 +479,7 @@ describe("HttpConnection", () => {
|
|||
}
|
||||
},
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => ({ connectionId: "42", availableTransports: [availableTransport] }))
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [availableTransport] }))
|
||||
.on("GET", (r) => {
|
||||
httpClientGetCount++;
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
|
|
@ -500,7 +498,7 @@ describe("HttpConnection", () => {
|
|||
throw new Error("fail");
|
||||
}
|
||||
})
|
||||
.on("DELETE", (r) => new HttpResponse(202)),
|
||||
.on("DELETE", () => new HttpResponse(202)),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -521,8 +519,8 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => ({ connectionId: "42", availableTransports: [availableTransport] }))
|
||||
.on("GET", (r) => {
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [availableTransport] }))
|
||||
.on("GET", () => {
|
||||
httpClientGetCount++;
|
||||
if (httpClientGetCount === 1) {
|
||||
// First long polling request must succeed so start completes
|
||||
|
|
@ -531,7 +529,7 @@ describe("HttpConnection", () => {
|
|||
throw new Error("fail");
|
||||
}
|
||||
})
|
||||
.on("DELETE", (r) => new HttpResponse(202)),
|
||||
.on("DELETE", () => new HttpResponse(202)),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -549,7 +547,7 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => ({ connectionId: "42", availableTransports: [serverSentEventsTransport] })),
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [serverSentEventsTransport] })),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
|
@ -565,12 +563,12 @@ describe("HttpConnection", () => {
|
|||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient()
|
||||
.on("POST", (r) => ({ connectionId: "42", availableTransports: [webSocketsTransport] })),
|
||||
.on("POST", () => ({ connectionId: "42", availableTransports: [webSocketsTransport] })),
|
||||
} as IHttpConnectionOptions;
|
||||
|
||||
const connection = new HttpConnection("http://tempuri.org", options);
|
||||
|
||||
expect(connection.start(TransferFormat.Text))
|
||||
await expect(connection.start(TransferFormat.Text))
|
||||
.rejects
|
||||
.toThrow("Unable to initialize any of the available transports.");
|
||||
});
|
||||
|
|
@ -584,7 +582,7 @@ describe("HttpConnection", () => {
|
|||
it("uses global WebSocket if defined", async () => {
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
global["WebSocket"] = class WebSocket {
|
||||
constructor(url: string, protocols?: string | string[]) {
|
||||
constructor() {
|
||||
throw new Error("WebSocket constructor called.");
|
||||
}
|
||||
};
|
||||
|
|
@ -609,7 +607,7 @@ describe("HttpConnection", () => {
|
|||
let eventSourceConstructorCalled: boolean = false;
|
||||
// tslint:disable-next-line:no-string-literal
|
||||
global["EventSource"] = class EventSource {
|
||||
constructor(url: string, eventSourceInitDict?: EventSourceInit) {
|
||||
constructor() {
|
||||
eventSourceConstructorCalled = true;
|
||||
throw new Error("EventSource constructor called.");
|
||||
}
|
||||
|
|
@ -617,7 +615,7 @@ describe("HttpConnection", () => {
|
|||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
httpClient: new TestHttpClient().on("POST", (r) => {
|
||||
httpClient: new TestHttpClient().on("POST", () => {
|
||||
return {
|
||||
availableTransports: [
|
||||
{ transport: "ServerSentEvents", transferFormats: ["Text"] },
|
||||
|
|
@ -643,17 +641,19 @@ describe("HttpConnection", () => {
|
|||
it("uses EventSource constructor from options if provided", async () => {
|
||||
let eventSourceConstructorCalled: boolean = false;
|
||||
|
||||
const customEventSourceType = class EventSource {
|
||||
constructor(url: string, eventSourceInitDict?: EventSourceInit) {
|
||||
class TestEventSource {
|
||||
// The "_" prefix tell TypeScript not to worry about unused parameter, but tslint doesn't like it.
|
||||
// tslint:disable-next-line:variable-name
|
||||
constructor(_url: string, _eventSourceInitDict: EventSourceInit) {
|
||||
eventSourceConstructorCalled = true;
|
||||
throw new Error("EventSource constructor called.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
EventSource: customEventSourceType as EventSourceConstructor,
|
||||
httpClient: new TestHttpClient().on("POST", (r) => {
|
||||
EventSource: TestEventSource as EventSourceConstructor,
|
||||
httpClient: new TestHttpClient().on("POST", () => {
|
||||
return {
|
||||
availableTransports: [
|
||||
{ transport: "ServerSentEvents", transferFormats: ["Text"] },
|
||||
|
|
@ -674,15 +674,17 @@ describe("HttpConnection", () => {
|
|||
});
|
||||
|
||||
it("uses WebSocket constructor from options if provided", async () => {
|
||||
const customWebSocketType = class WebSocket {
|
||||
constructor(url: string, protocols?: string | string[]) {
|
||||
class TestWebSocket {
|
||||
// The "_" prefix tell TypeScript not to worry about unused parameter, but tslint doesn't like it.
|
||||
// tslint:disable-next-line:variable-name
|
||||
constructor(_url: string, _protocols?: string | string[]) {
|
||||
throw new Error("WebSocket constructor called.");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const options: IHttpConnectionOptions = {
|
||||
...commonOptions,
|
||||
WebSocket: customWebSocketType as WebSocketConstructor,
|
||||
WebSocket: TestWebSocket,
|
||||
skipNegotiation: true,
|
||||
transport: HttpTransportType.WebSockets,
|
||||
} as IHttpConnectionOptions;
|
||||
|
|
|
|||
|
|
@ -88,7 +88,9 @@ describe("HubConnection", () => {
|
|||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
try {
|
||||
const invokePromise = hubConnection.send("testMethod", "arg", 42)
|
||||
// We don't actually care to wait for the send.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
hubConnection.send("testMethod", "arg", 42)
|
||||
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
|
||||
|
||||
// Verify the message is sent
|
||||
|
|
@ -103,7 +105,7 @@ describe("HubConnection", () => {
|
|||
});
|
||||
} finally {
|
||||
// Close the connection
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -114,7 +116,9 @@ describe("HubConnection", () => {
|
|||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
try {
|
||||
const invokePromise = hubConnection.invoke("testMethod", "arg", 42)
|
||||
// We don't actually care to wait for the send.
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
hubConnection.invoke("testMethod", "arg", 42)
|
||||
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
|
||||
|
||||
// Verify the message is sent
|
||||
|
|
@ -131,7 +135,7 @@ describe("HubConnection", () => {
|
|||
|
||||
} finally {
|
||||
// Close the connection
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -153,7 +157,7 @@ describe("HubConnection", () => {
|
|||
// message only contained handshake response
|
||||
expect(protocolCalled).toEqual(false);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -176,7 +180,7 @@ describe("HubConnection", () => {
|
|||
// message only contained handshake response
|
||||
expect(protocolCalled).toEqual(false);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -204,7 +208,7 @@ describe("HubConnection", () => {
|
|||
// left over data is the message pack message
|
||||
expect(receivedProcotolData!.byteLength).toEqual(102);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -223,7 +227,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(receivedProcotolData).toEqual("{\"type\":6}" + TextMessageFormat.RecordSeparator);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -239,7 +243,7 @@ describe("HubConnection", () => {
|
|||
|
||||
await expect(invokePromise).rejects.toThrow("foo");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -255,7 +259,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(await invokePromise).toBe("foo");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -267,9 +271,9 @@ describe("HubConnection", () => {
|
|||
connection.receiveHandshakeResponse();
|
||||
|
||||
const invokePromise = hubConnection.invoke("testMethod");
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
|
||||
expect(invokePromise).rejects.toThrow("Invocation canceled due to connection being closed.");
|
||||
await expect(invokePromise).rejects.toThrow("Invocation canceled due to connection being closed.");
|
||||
});
|
||||
|
||||
it("completes pending invocations when connection is lost", async () => {
|
||||
|
|
@ -283,9 +287,9 @@ describe("HubConnection", () => {
|
|||
// Typically this would be called by the transport
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
|
||||
expect(invokePromise).rejects.toThrow("Connection lost");
|
||||
await expect(invokePromise).rejects.toThrow("Connection lost");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -314,7 +318,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(warnings).toEqual(["No client method with the name 'message' found."]);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -346,7 +350,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(warnings).toEqual(["No client method with the name 'message' found."]);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -382,7 +386,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(count).toBe(2);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -418,7 +422,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(count).toBe(3);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -443,7 +447,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(count).toBe(1);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -466,7 +470,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(value).toBe("test");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -481,7 +485,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(closeError!.message).toEqual("Server returned handshake error: Error!");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -505,7 +509,7 @@ describe("HubConnection", () => {
|
|||
expect(isClosed).toEqual(true);
|
||||
expect(closeError).toBeUndefined();
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -530,7 +534,7 @@ describe("HubConnection", () => {
|
|||
expect(isClosed).toEqual(true);
|
||||
expect(closeError!.message).toEqual("Server returned an error on close: Error!");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -556,7 +560,7 @@ describe("HubConnection", () => {
|
|||
expect(numInvocations1).toBe(1);
|
||||
expect(numInvocations2).toBe(1);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -590,7 +594,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(numInvocations).toBe(1);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -602,7 +606,7 @@ describe("HubConnection", () => {
|
|||
hubConnection.on("message", (t) => { });
|
||||
hubConnection.on("message", () => { });
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -647,7 +651,7 @@ describe("HubConnection", () => {
|
|||
hubConnection.off(null!, () => { });
|
||||
hubConnection.off(undefined!, () => { });
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -658,7 +662,7 @@ describe("HubConnection", () => {
|
|||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
try {
|
||||
const invokePromise = hubConnection.stream("testStream", "arg", 42);
|
||||
hubConnection.stream("testStream", "arg", 42);
|
||||
|
||||
// Verify the message is sent
|
||||
expect(connection.sentData.length).toBe(1);
|
||||
|
|
@ -673,9 +677,9 @@ describe("HubConnection", () => {
|
|||
});
|
||||
|
||||
// Close the connection
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -691,9 +695,9 @@ describe("HubConnection", () => {
|
|||
|
||||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, error: "foo" });
|
||||
|
||||
expect(observer.completed).rejects.toThrow("Error: foo");
|
||||
await expect(observer.completed).rejects.toThrow("Error: foo");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -711,7 +715,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(await observer.completed).toEqual([]);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -723,11 +727,11 @@ describe("HubConnection", () => {
|
|||
const observer = new TestObserver();
|
||||
hubConnection.stream<any>("testMethod")
|
||||
.subscribe(observer);
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
|
||||
expect(observer.completed).rejects.toThrow("Error: Invocation canceled due to connection being closed.");
|
||||
await expect(observer.completed).rejects.toThrow("Error: Invocation canceled due to connection being closed.");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -743,9 +747,9 @@ describe("HubConnection", () => {
|
|||
// Typically this would be called by the transport
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
|
||||
expect(observer.completed).rejects.toThrow("Error: Connection lost");
|
||||
await expect(observer.completed).rejects.toThrow("Error: Connection lost");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -771,7 +775,7 @@ describe("HubConnection", () => {
|
|||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId });
|
||||
expect(await observer.completed).toEqual([1, 2, 3]);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -780,13 +784,13 @@ describe("HubConnection", () => {
|
|||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
try {
|
||||
const observer = hubConnection.stream("testMethod").subscribe(NullSubscriber.instance);
|
||||
hubConnection.stream("testMethod").subscribe(NullSubscriber.instance);
|
||||
|
||||
// Typically this would be called by the transport
|
||||
// triggers observer.error()
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -795,17 +799,17 @@ describe("HubConnection", () => {
|
|||
|
||||
const hubConnection = createHubConnection(connection);
|
||||
try {
|
||||
const observer = hubConnection.stream("testMethod").subscribe(NullSubscriber.instance);
|
||||
hubConnection.stream("testMethod").subscribe(NullSubscriber.instance);
|
||||
|
||||
// Send completion to trigger observer.complete()
|
||||
// Expectation is connection.receive will not to throw
|
||||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId });
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
it("can be canceled", () => {
|
||||
it("can be canceled", async () => {
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = createHubConnection(connection);
|
||||
try {
|
||||
|
|
@ -831,7 +835,7 @@ describe("HubConnection", () => {
|
|||
type: MessageType.CancelInvocation,
|
||||
});
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -848,7 +852,7 @@ describe("HubConnection", () => {
|
|||
connection.onclose!();
|
||||
expect(invocations).toBe(2);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -863,7 +867,7 @@ describe("HubConnection", () => {
|
|||
connection.onclose!(new Error("Test error."));
|
||||
expect(error!.message).toBe("Test error.");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -877,7 +881,7 @@ describe("HubConnection", () => {
|
|||
connection.onclose!();
|
||||
// expect no errors
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -892,7 +896,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(state).toBe(HubConnectionState.Disconnected);
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
@ -911,7 +915,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(await invokePromise).toBe("foo");
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -930,13 +934,13 @@ describe("HubConnection", () => {
|
|||
await pingAndWait(connection);
|
||||
}
|
||||
|
||||
connection.stop();
|
||||
await connection.stop();
|
||||
|
||||
const error = await p.promise;
|
||||
|
||||
expect(error).toBeUndefined();
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -959,13 +963,13 @@ describe("HubConnection", () => {
|
|||
await pingAndWait(connection);
|
||||
}
|
||||
|
||||
connection.stop();
|
||||
await connection.stop();
|
||||
|
||||
const error = await p.promise;
|
||||
|
||||
expect(error).toBeUndefined();
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -984,7 +988,7 @@ describe("HubConnection", () => {
|
|||
|
||||
expect(error).toEqual(new Error("Server timeout elapsed without receiving a message from the server."));
|
||||
} finally {
|
||||
hubConnection.stop();
|
||||
await hubConnection.stop();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,15 +13,6 @@ import { NullLogger } from "../src/Loggers";
|
|||
import { TestHttpClient } from "./TestHttpClient";
|
||||
import { PromiseSource } from "./Utils";
|
||||
|
||||
const allTransportsNegotiateResponse = {
|
||||
availableTransports: [
|
||||
{ transport: "WebSockets", transferFormats: ["Text", "Binary"] },
|
||||
{ transport: "ServerSentEvents", transferFormats: ["Text"] },
|
||||
{ transport: "LongPolling", transferFormats: ["Text", "Binary"] },
|
||||
],
|
||||
connectionId: "abc123",
|
||||
};
|
||||
|
||||
const longPollingNegotiateResponse = {
|
||||
availableTransports: [
|
||||
{ transport: "LongPolling", transferFormats: ["Text", "Binary"] },
|
||||
|
|
@ -80,11 +71,6 @@ describe("HubConnectionBuilder", () => {
|
|||
it("can configure transport type", async () => {
|
||||
const protocol = new TestProtocol();
|
||||
|
||||
const pollSent = new PromiseSource<HttpRequest>();
|
||||
const pollCompleted = new PromiseSource<HttpResponse>();
|
||||
const negotiateReceived = new PromiseSource<HttpRequest>();
|
||||
const testClient = createTestClient(pollSent, pollCompleted.promise, allTransportsNegotiateResponse);
|
||||
|
||||
const builder = createConnectionBuilder()
|
||||
.withUrl("http://example.com", HttpTransportType.WebSockets)
|
||||
.withHubProtocol(protocol);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import { HttpError, TimeoutError } from "../src/Errors";
|
||||
// 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 { HttpResponse } from "../src/HttpClient";
|
||||
import { LogLevel } from "../src/ILogger";
|
||||
import { TransferFormat } from "../src/ITransport";
|
||||
import { NullLogger } from "../src/Loggers";
|
||||
import { LongPollingTransport } from "../src/LongPollingTransport";
|
||||
import { ConsoleLogger } from "../src/Utils";
|
||||
|
||||
import { TestHttpClient } from "./TestHttpClient";
|
||||
import { delay, PromiseSource, SyncPoint } from "./Utils";
|
||||
import { PromiseSource, SyncPoint } from "./Utils";
|
||||
|
||||
describe("LongPollingTransport", () => {
|
||||
it("shuts down polling by aborting in-progress request", async () => {
|
||||
|
|
@ -29,7 +29,7 @@ describe("LongPollingTransport", () => {
|
|||
return new HttpResponse(200);
|
||||
}
|
||||
})
|
||||
.on("DELETE", (r) => new HttpResponse(202));
|
||||
.on("DELETE", () => new HttpResponse(202));
|
||||
const transport = new LongPollingTransport(client, undefined, NullLogger.instance, false);
|
||||
|
||||
await transport.connect("http://example.com", TransferFormat.Text);
|
||||
|
|
@ -42,17 +42,16 @@ describe("LongPollingTransport", () => {
|
|||
|
||||
it("204 server response stops polling and raises onClose", async () => {
|
||||
let firstPoll = true;
|
||||
let onCloseCalled = false;
|
||||
const client = new TestHttpClient()
|
||||
.on("GET", async (r) => {
|
||||
if (firstPoll) {
|
||||
firstPoll = false;
|
||||
return new HttpResponse(200);
|
||||
} else {
|
||||
// A 204 response will stop the long polling transport
|
||||
return new HttpResponse(204);
|
||||
}
|
||||
});
|
||||
.on("GET", async () => {
|
||||
if (firstPoll) {
|
||||
firstPoll = false;
|
||||
return new HttpResponse(200);
|
||||
} else {
|
||||
// A 204 response will stop the long polling transport
|
||||
return new HttpResponse(204);
|
||||
}
|
||||
});
|
||||
const transport = new LongPollingTransport(client, undefined, NullLogger.instance, false);
|
||||
|
||||
const stopPromise = makeClosedPromise(transport);
|
||||
|
|
@ -83,7 +82,7 @@ describe("LongPollingTransport", () => {
|
|||
await deleteSyncPoint.waitToContinue();
|
||||
return new HttpResponse(202);
|
||||
});
|
||||
|
||||
|
||||
const transport = new LongPollingTransport(httpClient, undefined, NullLogger.instance, false);
|
||||
|
||||
await transport.connect("http://tempuri.org", TransferFormat.Text);
|
||||
|
|
@ -119,4 +118,4 @@ function makeClosedPromise(transport: LongPollingTransport): Promise<void> {
|
|||
}
|
||||
};
|
||||
return closed.promise;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,4 +60,4 @@ export class SyncPoint {
|
|||
this.atSyncPoint.resolve();
|
||||
return this.continueFromSyncPoint.promise;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
"interface-name": false,
|
||||
"unified-signatures": false,
|
||||
"max-classes-per-file": false,
|
||||
"no-floating-promises": true,
|
||||
"no-unused-variable": [true, {"ignore-pattern": "^_"}],
|
||||
"no-empty": false,
|
||||
"no-bitwise": false,
|
||||
"no-console": false
|
||||
|
|
|
|||
Loading…
Reference in New Issue