Skip to content

Commit a1e77a2

Browse files
Set transport handlers earlier in Typescript client (#12524)
1 parent 2d4fd05 commit a1e77a2

File tree

2 files changed

+87
-6
lines changed

2 files changed

+87
-6
lines changed

src/SignalR/clients/ts/signalr/src/HttpConnection.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ export class HttpConnection implements IConnection {
230230
this.transport = this.constructTransport(HttpTransportType.WebSockets);
231231
// We should just call connect directly in this case.
232232
// No fallback or negotiate in this case.
233-
await this.transport!.connect(url, transferFormat);
233+
await this.startTransport(url, transferFormat);
234234
} else {
235235
throw new Error("Negotiation can only be skipped when using the WebSocket transport directly.");
236236
}
@@ -281,9 +281,6 @@ export class HttpConnection implements IConnection {
281281
this.features.inherentKeepAlive = true;
282282
}
283283

284-
this.transport!.onreceive = this.onreceive;
285-
this.transport!.onclose = (e) => this.stopConnection(e);
286-
287284
if (this.connectionState === ConnectionState.Connecting) {
288285
// Ensure the connection transitions to the connected state prior to completing this.startInternalPromise.
289286
// start() will handle the case when stop was called and startInternal exits still in the disconnecting state.
@@ -344,7 +341,7 @@ export class HttpConnection implements IConnection {
344341
if (this.isITransport(requestedTransport)) {
345342
this.logger.log(LogLevel.Debug, "Connection was provided an instance of ITransport, using that directly.");
346343
this.transport = requestedTransport;
347-
await this.transport.connect(connectUrl, requestedTransferFormat);
344+
await this.startTransport(connectUrl, requestedTransferFormat);
348345

349346
return;
350347
}
@@ -367,7 +364,7 @@ export class HttpConnection implements IConnection {
367364
connectUrl = this.createConnectUrl(url, negotiateResponse.connectionId);
368365
}
369366
try {
370-
await this.transport!.connect(connectUrl, requestedTransferFormat);
367+
await this.startTransport(connectUrl, requestedTransferFormat);
371368
return;
372369
} catch (ex) {
373370
this.logger.log(LogLevel.Error, `Failed to start the transport '${endpoint.transport}': ${ex}`);
@@ -408,6 +405,12 @@ export class HttpConnection implements IConnection {
408405
}
409406
}
410407

408+
private startTransport(url: string, transferFormat: TransferFormat): Promise<void> {
409+
this.transport!.onreceive = this.onreceive;
410+
this.transport!.onclose = (e) => this.stopConnection(e);
411+
return this.transport!.connect(url, transferFormat);
412+
}
413+
411414
private resolveTransportOrError(endpoint: IAvailableTransport, requestedTransport: HttpTransportType | undefined, requestedTransferFormat: TransferFormat): ITransport | Error {
412415
const transport = HttpTransportType[endpoint.transport];
413416
if (transport === null || transport === undefined) {

src/SignalR/clients/ts/signalr/tests/HttpConnection.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,84 @@ describe("HttpConnection", () => {
795795
});
796796
});
797797

798+
it("transport handlers set before start", async () => {
799+
await VerifyLogger.run(async (logger) => {
800+
const availableTransport = { transport: "LongPolling", transferFormats: ["Text"] };
801+
let handlersSet = false;
802+
803+
let httpClientGetCount = 0;
804+
const options: IHttpConnectionOptions = {
805+
...commonOptions,
806+
httpClient: new TestHttpClient()
807+
.on("POST", () => ({ connectionId: "42", availableTransports: [availableTransport] }))
808+
.on("GET", () => {
809+
httpClientGetCount++;
810+
if (httpClientGetCount === 1) {
811+
if ((connection as any).transport.onreceive && (connection as any).transport.onclose) {
812+
handlersSet = true;
813+
}
814+
// First long polling request must succeed so start completes
815+
return "";
816+
}
817+
})
818+
.on("DELETE", () => new HttpResponse(202)),
819+
logger,
820+
} as IHttpConnectionOptions;
821+
822+
const connection = new HttpConnection("http://tempuri.org", options);
823+
connection.onreceive = () => null;
824+
try {
825+
await connection.start(TransferFormat.Text);
826+
} finally {
827+
await connection.stop();
828+
}
829+
830+
expect(handlersSet).toBe(true);
831+
});
832+
});
833+
834+
it("transport handlers set before start for custom transports", async () => {
835+
await VerifyLogger.run(async (logger) => {
836+
const availableTransport = { transport: "Custom", transferFormats: ["Text"] };
837+
let handlersSet = false;
838+
const transport: ITransport = {
839+
connect: (url: string, transferFormat: TransferFormat) => {
840+
if (transport.onreceive && transport.onclose) {
841+
handlersSet = true;
842+
}
843+
return Promise.resolve();
844+
},
845+
onclose: null,
846+
onreceive: null,
847+
send: (data: any) => Promise.resolve(),
848+
stop: () => {
849+
if (transport.onclose) {
850+
transport.onclose();
851+
}
852+
return Promise.resolve();
853+
},
854+
};
855+
856+
const options: IHttpConnectionOptions = {
857+
...commonOptions,
858+
httpClient: new TestHttpClient()
859+
.on("POST", () => ({ connectionId: "42", availableTransports: [availableTransport] })),
860+
logger,
861+
transport,
862+
} as IHttpConnectionOptions;
863+
864+
const connection = new HttpConnection("http://tempuri.org", options);
865+
connection.onreceive = () => null;
866+
try {
867+
await connection.start(TransferFormat.Text);
868+
} finally {
869+
await connection.stop();
870+
}
871+
872+
expect(handlersSet).toBe(true);
873+
});
874+
});
875+
798876
describe(".constructor", () => {
799877
it("throws if no Url is provided", async () => {
800878
// Force TypeScript to let us call the constructor incorrectly :)

0 commit comments

Comments
 (0)