Add 'withCredentials' option to TS client (#15076)
This commit is contained in:
parent
d34dc80e02
commit
c45510c8cb
|
|
@ -4,6 +4,7 @@
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Connections;
|
using Microsoft.AspNetCore.Connections;
|
||||||
|
using Microsoft.AspNetCore.Http.Connections;
|
||||||
|
|
||||||
namespace FunctionalTests
|
namespace FunctionalTests
|
||||||
{
|
{
|
||||||
|
|
@ -11,6 +12,13 @@ namespace FunctionalTests
|
||||||
{
|
{
|
||||||
public async override Task OnConnectedAsync(ConnectionContext connection)
|
public async override Task OnConnectedAsync(ConnectionContext connection)
|
||||||
{
|
{
|
||||||
|
var context = connection.GetHttpContext();
|
||||||
|
// The 'withCredentials' tests wont send a cookie for cross-site requests
|
||||||
|
if (!context.WebSockets.IsWebSocketRequest && !context.Request.Cookies.ContainsKey("testCookie"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var result = await connection.Transport.Input.ReadAsync();
|
var result = await connection.Transport.Input.ReadAsync();
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { TestLogger } from "./TestLogger";
|
||||||
|
|
||||||
// We want to continue testing HttpConnection, but we don't export it anymore. So just pull it in directly from the source file.
|
// We want to continue testing HttpConnection, but we don't export it anymore. So just pull it in directly from the source file.
|
||||||
import { HttpConnection } from "@microsoft/signalr/dist/esm/HttpConnection";
|
import { HttpConnection } from "@microsoft/signalr/dist/esm/HttpConnection";
|
||||||
|
import { Platform } from "@microsoft/signalr/dist/esm/Utils";
|
||||||
import "./LogBannerReporter";
|
import "./LogBannerReporter";
|
||||||
|
|
||||||
const commonOptions: IHttpConnectionOptions = {
|
const commonOptions: IHttpConnectionOptions = {
|
||||||
|
|
@ -153,6 +154,41 @@ describe("connection", () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// withCredentials doesn't make sense in Node or when using WebSockets
|
||||||
|
if (!Platform.isNode && transportType !== HttpTransportType.WebSockets &&
|
||||||
|
// tests run through karma during automation which is cross-site, but manually running the server will result in these tests failing
|
||||||
|
// so we check for cross-site
|
||||||
|
!(window && ECHOENDPOINT_URL.match(`^${window.location.href}`))) {
|
||||||
|
it("honors withCredentials flag", (done) => {
|
||||||
|
TestLogger.saveLogsAndReset();
|
||||||
|
const message = "Hello World!";
|
||||||
|
|
||||||
|
// The server will set some response headers for the '/negotiate' endpoint
|
||||||
|
const connection = new HttpConnection(ECHOENDPOINT_URL, {
|
||||||
|
...commonOptions,
|
||||||
|
httpClient,
|
||||||
|
transport: transportType,
|
||||||
|
withCredentials: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
connection.onreceive = (data: any) => {
|
||||||
|
fail(new Error(`Unexpected messaged received '${data}'.`));
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-ignore: We don't use the error parameter intentionally.
|
||||||
|
connection.onclose = (error) => {
|
||||||
|
done();
|
||||||
|
};
|
||||||
|
|
||||||
|
connection.start(TransferFormat.Text).then(() => {
|
||||||
|
connection.send(message);
|
||||||
|
}).catch((e: any) => {
|
||||||
|
fail(e);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ export class FetchHttpClient extends HttpClient {
|
||||||
response = await fetch(request.url!, {
|
response = await fetch(request.url!, {
|
||||||
body: request.content!,
|
body: request.content!,
|
||||||
cache: "no-cache",
|
cache: "no-cache",
|
||||||
credentials: "include",
|
credentials: request.withCredentials === true ? "include" : "same-origin",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/plain;charset=UTF-8",
|
"Content-Type": "text/plain;charset=UTF-8",
|
||||||
"X-Requested-With": "XMLHttpRequest",
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ export interface HttpRequest {
|
||||||
|
|
||||||
/** The time to wait for the request to complete before throwing a TimeoutError. Measured in milliseconds. */
|
/** The time to wait for the request to complete before throwing a TimeoutError. Measured in milliseconds. */
|
||||||
timeout?: number;
|
timeout?: number;
|
||||||
|
|
||||||
|
/** This controls whether credentials such as cookies are sent in cross-site requests. */
|
||||||
|
withCredentials?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Represents an HTTP response. */
|
/** Represents an HTTP response. */
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,12 @@ export class HttpConnection implements IConnection {
|
||||||
this.baseUrl = this.resolveUrl(url);
|
this.baseUrl = this.resolveUrl(url);
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.logMessageContent = options.logMessageContent || false;
|
options.logMessageContent = options.logMessageContent === undefined ? false : options.logMessageContent;
|
||||||
|
if (typeof options.withCredentials === "boolean" || options.withCredentials === undefined) {
|
||||||
|
options.withCredentials = options.withCredentials === undefined ? true : options.withCredentials;
|
||||||
|
} else {
|
||||||
|
throw new Error("withCredentials option was not a 'boolean' or 'undefined' value");
|
||||||
|
}
|
||||||
|
|
||||||
if (!Platform.isNode && typeof WebSocket !== "undefined" && !options.WebSocket) {
|
if (!Platform.isNode && typeof WebSocket !== "undefined" && !options.WebSocket) {
|
||||||
options.WebSocket = WebSocket;
|
options.WebSocket = WebSocket;
|
||||||
|
|
@ -319,6 +324,7 @@ export class HttpConnection implements IConnection {
|
||||||
const response = await this.httpClient.post(negotiateUrl, {
|
const response = await this.httpClient.post(negotiateUrl, {
|
||||||
content: "",
|
content: "",
|
||||||
headers,
|
headers,
|
||||||
|
withCredentials: this.options.withCredentials,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.statusCode !== 200) {
|
if (response.statusCode !== 200) {
|
||||||
|
|
@ -410,9 +416,9 @@ export class HttpConnection implements IConnection {
|
||||||
if (!this.options.EventSource) {
|
if (!this.options.EventSource) {
|
||||||
throw new Error("'EventSource' is not supported in your environment.");
|
throw new Error("'EventSource' is not supported in your environment.");
|
||||||
}
|
}
|
||||||
return new ServerSentEventsTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.EventSource);
|
return new ServerSentEventsTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.EventSource, this.options.withCredentials!);
|
||||||
case HttpTransportType.LongPolling:
|
case HttpTransportType.LongPolling:
|
||||||
return new LongPollingTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false);
|
return new LongPollingTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.withCredentials!);
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown transport: ${transport}.`);
|
throw new Error(`Unknown transport: ${transport}.`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,4 +53,12 @@ export interface IHttpConnectionOptions {
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
EventSource?: EventSourceConstructor;
|
EventSource?: EventSourceConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value is 'true'.
|
||||||
|
* This controls whether credentials such as cookies are sent in cross-site requests.
|
||||||
|
*
|
||||||
|
* Cookies are used by many load-balancers for sticky sessions which is required when your app is deployed with multiple servers.
|
||||||
|
*/
|
||||||
|
withCredentials?: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export class LongPollingTransport implements ITransport {
|
||||||
private readonly accessTokenFactory: (() => string | Promise<string>) | undefined;
|
private readonly accessTokenFactory: (() => string | Promise<string>) | undefined;
|
||||||
private readonly logger: ILogger;
|
private readonly logger: ILogger;
|
||||||
private readonly logMessageContent: boolean;
|
private readonly logMessageContent: boolean;
|
||||||
|
private readonly withCredentials: boolean;
|
||||||
private readonly pollAbort: AbortController;
|
private readonly pollAbort: AbortController;
|
||||||
|
|
||||||
private url?: string;
|
private url?: string;
|
||||||
|
|
@ -30,12 +31,13 @@ export class LongPollingTransport implements ITransport {
|
||||||
return this.pollAbort.aborted;
|
return this.pollAbort.aborted;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger, logMessageContent: boolean) {
|
constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger, logMessageContent: boolean, withCredentials: boolean) {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.accessTokenFactory = accessTokenFactory;
|
this.accessTokenFactory = accessTokenFactory;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.pollAbort = new AbortController();
|
this.pollAbort = new AbortController();
|
||||||
this.logMessageContent = logMessageContent;
|
this.logMessageContent = logMessageContent;
|
||||||
|
this.withCredentials = withCredentials;
|
||||||
|
|
||||||
this.running = false;
|
this.running = false;
|
||||||
|
|
||||||
|
|
@ -66,6 +68,7 @@ export class LongPollingTransport implements ITransport {
|
||||||
abortSignal: this.pollAbort.signal,
|
abortSignal: this.pollAbort.signal,
|
||||||
headers,
|
headers,
|
||||||
timeout: 100000,
|
timeout: 100000,
|
||||||
|
withCredentials: this.withCredentials,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (transferFormat === TransferFormat.Binary) {
|
if (transferFormat === TransferFormat.Binary) {
|
||||||
|
|
@ -182,7 +185,7 @@ export class LongPollingTransport implements ITransport {
|
||||||
if (!this.running) {
|
if (!this.running) {
|
||||||
return Promise.reject(new Error("Cannot send until the transport is connected"));
|
return Promise.reject(new Error("Cannot send until the transport is connected"));
|
||||||
}
|
}
|
||||||
return sendMessage(this.logger, "LongPolling", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent);
|
return sendMessage(this.logger, "LongPolling", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent, this.withCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stop(): Promise<void> {
|
public async stop(): Promise<void> {
|
||||||
|
|
@ -204,6 +207,7 @@ export class LongPollingTransport implements ITransport {
|
||||||
|
|
||||||
const deleteOptions: HttpRequest = {
|
const deleteOptions: HttpRequest = {
|
||||||
headers,
|
headers,
|
||||||
|
withCredentials: this.withCredentials,
|
||||||
};
|
};
|
||||||
const token = await this.getAccessToken();
|
const token = await this.getAccessToken();
|
||||||
this.updateHeaderToken(deleteOptions, token);
|
this.updateHeaderToken(deleteOptions, token);
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
private readonly accessTokenFactory: (() => string | Promise<string>) | undefined;
|
private readonly accessTokenFactory: (() => string | Promise<string>) | undefined;
|
||||||
private readonly logger: ILogger;
|
private readonly logger: ILogger;
|
||||||
private readonly logMessageContent: boolean;
|
private readonly logMessageContent: boolean;
|
||||||
|
private readonly withCredentials: boolean;
|
||||||
private readonly eventSourceConstructor: EventSourceConstructor;
|
private readonly eventSourceConstructor: EventSourceConstructor;
|
||||||
private eventSource?: EventSource;
|
private eventSource?: EventSource;
|
||||||
private url?: string;
|
private url?: string;
|
||||||
|
|
@ -21,11 +22,12 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
public onclose: ((error?: Error) => void) | null;
|
public onclose: ((error?: Error) => void) | null;
|
||||||
|
|
||||||
constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger,
|
constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger,
|
||||||
logMessageContent: boolean, eventSourceConstructor: EventSourceConstructor) {
|
logMessageContent: boolean, eventSourceConstructor: EventSourceConstructor, withCredentials: boolean) {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.accessTokenFactory = accessTokenFactory;
|
this.accessTokenFactory = accessTokenFactory;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.logMessageContent = logMessageContent;
|
this.logMessageContent = logMessageContent;
|
||||||
|
this.withCredentials = withCredentials;
|
||||||
this.eventSourceConstructor = eventSourceConstructor;
|
this.eventSourceConstructor = eventSourceConstructor;
|
||||||
|
|
||||||
this.onreceive = null;
|
this.onreceive = null;
|
||||||
|
|
@ -58,7 +60,7 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
|
|
||||||
let eventSource: EventSource;
|
let eventSource: EventSource;
|
||||||
if (Platform.isBrowser || Platform.isWebWorker) {
|
if (Platform.isBrowser || Platform.isWebWorker) {
|
||||||
eventSource = new this.eventSourceConstructor(url, { withCredentials: true });
|
eventSource = new this.eventSourceConstructor(url, { withCredentials: this.withCredentials });
|
||||||
} else {
|
} else {
|
||||||
// Non-browser passes cookies via the dictionary
|
// Non-browser passes cookies via the dictionary
|
||||||
const cookies = this.httpClient.getCookieString(url);
|
const cookies = this.httpClient.getCookieString(url);
|
||||||
|
|
@ -68,7 +70,7 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
const [name, value] = getUserAgentHeader();
|
const [name, value] = getUserAgentHeader();
|
||||||
headers[name] = value;
|
headers[name] = value;
|
||||||
|
|
||||||
eventSource = new this.eventSourceConstructor(url, { withCredentials: true, headers } as EventSourceInit);
|
eventSource = new this.eventSourceConstructor(url, { withCredentials: this.withCredentials, headers } as EventSourceInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -110,7 +112,7 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
if (!this.eventSource) {
|
if (!this.eventSource) {
|
||||||
return Promise.reject(new Error("Cannot send until the transport is connected"));
|
return Promise.reject(new Error("Cannot send until the transport is connected"));
|
||||||
}
|
}
|
||||||
return sendMessage(this.logger, "SSE", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent);
|
return sendMessage(this.logger, "SSE", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent, this.withCredentials);
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop(): Promise<void> {
|
public stop(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,8 @@ export function isArrayBuffer(val: any): val is ArrayBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
export async function sendMessage(logger: ILogger, transportName: string, httpClient: HttpClient, url: string, accessTokenFactory: (() => string | Promise<string>) | undefined, content: string | ArrayBuffer, logMessageContent: boolean): Promise<void> {
|
export async function sendMessage(logger: ILogger, transportName: string, httpClient: HttpClient, url: string, accessTokenFactory: (() => string | Promise<string>) | undefined,
|
||||||
|
content: string | ArrayBuffer, logMessageContent: boolean, withCredentials: boolean): Promise<void> {
|
||||||
let headers = {};
|
let headers = {};
|
||||||
if (accessTokenFactory) {
|
if (accessTokenFactory) {
|
||||||
const token = await accessTokenFactory();
|
const token = await accessTokenFactory();
|
||||||
|
|
@ -105,6 +106,7 @@ export async function sendMessage(logger: ILogger, transportName: string, httpCl
|
||||||
content,
|
content,
|
||||||
headers,
|
headers,
|
||||||
responseType,
|
responseType,
|
||||||
|
withCredentials,
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.log(LogLevel.Trace, `(${transportName} transport) request complete. Response status: ${response.statusCode}.`);
|
logger.log(LogLevel.Trace, `(${transportName} transport) request complete. Response status: ${response.statusCode}.`);
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ export class XhrHttpClient extends HttpClient {
|
||||||
const xhr = new XMLHttpRequest();
|
const xhr = new XMLHttpRequest();
|
||||||
|
|
||||||
xhr.open(request.method!, request.url!, true);
|
xhr.open(request.method!, request.url!, true);
|
||||||
xhr.withCredentials = true;
|
xhr.withCredentials = request.withCredentials === undefined ? true : request.withCredentials;
|
||||||
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
|
||||||
// Explicitly setting the Content-Type header for React Native on Android platform.
|
// Explicitly setting the Content-Type header for React Native on Android platform.
|
||||||
xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
|
xhr.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ describe("LongPollingTransport", () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("DELETE", () => new HttpResponse(202));
|
.on("DELETE", () => new HttpResponse(202));
|
||||||
const transport = new LongPollingTransport(client, undefined, logger, false);
|
const transport = new LongPollingTransport(client, undefined, logger, false, true);
|
||||||
|
|
||||||
await transport.connect("http://example.com", TransferFormat.Text);
|
await transport.connect("http://example.com", TransferFormat.Text);
|
||||||
const stopPromise = transport.stop();
|
const stopPromise = transport.stop();
|
||||||
|
|
@ -64,7 +64,7 @@ describe("LongPollingTransport", () => {
|
||||||
return new HttpResponse(204);
|
return new HttpResponse(204);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const transport = new LongPollingTransport(client, undefined, logger, false);
|
const transport = new LongPollingTransport(client, undefined, logger, false, true);
|
||||||
|
|
||||||
const stopPromise = makeClosedPromise(transport);
|
const stopPromise = makeClosedPromise(transport);
|
||||||
|
|
||||||
|
|
@ -97,7 +97,7 @@ describe("LongPollingTransport", () => {
|
||||||
return new HttpResponse(202);
|
return new HttpResponse(202);
|
||||||
});
|
});
|
||||||
|
|
||||||
const transport = new LongPollingTransport(httpClient, undefined, logger, false);
|
const transport = new LongPollingTransport(httpClient, undefined, logger, false, true);
|
||||||
|
|
||||||
await transport.connect("http://tempuri.org", TransferFormat.Text);
|
await transport.connect("http://tempuri.org", TransferFormat.Text);
|
||||||
|
|
||||||
|
|
@ -146,7 +146,7 @@ describe("LongPollingTransport", () => {
|
||||||
return new HttpResponse(202);
|
return new HttpResponse(202);
|
||||||
});
|
});
|
||||||
|
|
||||||
const transport = new LongPollingTransport(httpClient, undefined, logger, false);
|
const transport = new LongPollingTransport(httpClient, undefined, logger, false, true);
|
||||||
|
|
||||||
await transport.connect("http://tempuri.org", TransferFormat.Text);
|
await transport.connect("http://tempuri.org", TransferFormat.Text);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ registerUnhandledRejectionHandler();
|
||||||
describe("ServerSentEventsTransport", () => {
|
describe("ServerSentEventsTransport", () => {
|
||||||
it("does not allow non-text formats", async () => {
|
it("does not allow non-text formats", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource);
|
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource, true);
|
||||||
|
|
||||||
await expect(sse.connect("", TransferFormat.Binary))
|
await expect(sse.connect("", TransferFormat.Binary))
|
||||||
.rejects
|
.rejects
|
||||||
|
|
@ -27,7 +27,7 @@ describe("ServerSentEventsTransport", () => {
|
||||||
|
|
||||||
it("connect waits for EventSource to be connected", async () => {
|
it("connect waits for EventSource to be connected", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource);
|
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource, true);
|
||||||
|
|
||||||
let connectComplete: boolean = false;
|
let connectComplete: boolean = false;
|
||||||
const connectPromise = (async () => {
|
const connectPromise = (async () => {
|
||||||
|
|
@ -169,7 +169,7 @@ describe("ServerSentEventsTransport", () => {
|
||||||
|
|
||||||
it("send throws if not connected", async () => {
|
it("send throws if not connected", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource);
|
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource, true);
|
||||||
|
|
||||||
await expect(sse.send(""))
|
await expect(sse.send(""))
|
||||||
.rejects
|
.rejects
|
||||||
|
|
@ -224,7 +224,7 @@ describe("ServerSentEventsTransport", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function createAndStartSSE(logger: ILogger, url?: string, accessTokenFactory?: (() => string | Promise<string>), httpClient?: HttpClient): Promise<ServerSentEventsTransport> {
|
async function createAndStartSSE(logger: ILogger, url?: string, accessTokenFactory?: (() => string | Promise<string>), httpClient?: HttpClient): Promise<ServerSentEventsTransport> {
|
||||||
const sse = new ServerSentEventsTransport(httpClient || new TestHttpClient(), accessTokenFactory, logger, true, TestEventSource);
|
const sse = new ServerSentEventsTransport(httpClient || new TestHttpClient(), accessTokenFactory, logger, true, TestEventSource, true);
|
||||||
|
|
||||||
const connectPromise = sse.connect(url || "http://example.com", TransferFormat.Text);
|
const connectPromise = sse.connect(url || "http://example.com", TransferFormat.Text);
|
||||||
await TestEventSource.eventSource.openSet;
|
await TestEventSource.eventSource.openSet;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue