Add custom header support to Typescript client (#19227)
This commit is contained in:
parent
d225883c7d
commit
3fe9012821
|
|
@ -1123,6 +1123,34 @@ describe("hubConnection", () => {
|
||||||
fail(e);
|
fail(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("overwrites library headers with user headers", async (done) => {
|
||||||
|
const [name] = getUserAgentHeader();
|
||||||
|
const headers = { [name]: "Custom Agent", "X-HEADER": "VALUE" };
|
||||||
|
const hubConnection = getConnectionBuilder(t, TESTHUBENDPOINT_URL, { headers })
|
||||||
|
.withHubProtocol(new JsonHubProtocol())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await hubConnection.start();
|
||||||
|
|
||||||
|
const customUserHeader = await hubConnection.invoke("GetHeader", "X-HEADER");
|
||||||
|
const headerValue = await hubConnection.invoke("GetHeader", name);
|
||||||
|
|
||||||
|
if ((t === HttpTransportType.ServerSentEvents || t === HttpTransportType.WebSockets) && !Platform.isNode) {
|
||||||
|
expect(headerValue).toBeNull();
|
||||||
|
expect(customUserHeader).toBeNull();
|
||||||
|
} else {
|
||||||
|
expect(headerValue).toEqual("Custom Agent");
|
||||||
|
expect(customUserHeader).toEqual("VALUE");
|
||||||
|
}
|
||||||
|
|
||||||
|
await hubConnection.stop();
|
||||||
|
done();
|
||||||
|
} catch (e) {
|
||||||
|
fail(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function getJwtToken(url: string): Promise<string> {
|
function getJwtToken(url: string): Promise<string> {
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ To use the client in a NodeJS application, install the package to your `node_mod
|
||||||
|
|
||||||
### Example (Browser)
|
### Example (Browser)
|
||||||
|
|
||||||
```JavaScript
|
```javascript
|
||||||
let connection = new signalR.HubConnectionBuilder()
|
let connection = new signalR.HubConnectionBuilder()
|
||||||
.withUrl("/chat")
|
.withUrl("/chat")
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -51,8 +51,7 @@ connection.start()
|
||||||
|
|
||||||
### Example (WebWorker)
|
### Example (WebWorker)
|
||||||
|
|
||||||
|
```javascript
|
||||||
```JavaScript
|
|
||||||
importScripts('signalr.js');
|
importScripts('signalr.js');
|
||||||
|
|
||||||
let connection = new signalR.HubConnectionBuilder()
|
let connection = new signalR.HubConnectionBuilder()
|
||||||
|
|
@ -70,7 +69,7 @@ connection.start()
|
||||||
|
|
||||||
### Example (NodeJS)
|
### Example (NodeJS)
|
||||||
|
|
||||||
```JavaScript
|
```javascript
|
||||||
const signalR = require("@microsoft/signalr");
|
const signalR = require("@microsoft/signalr");
|
||||||
|
|
||||||
let connection = new signalR.HubConnectionBuilder()
|
let connection = new signalR.HubConnectionBuilder()
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
import { AbortSignal } from "./AbortController";
|
import { AbortSignal } from "./AbortController";
|
||||||
|
import { MessageHeaders } from "./IHubProtocol";
|
||||||
|
|
||||||
/** Represents an HTTP request. */
|
/** Represents an HTTP request. */
|
||||||
export interface HttpRequest {
|
export interface HttpRequest {
|
||||||
|
|
@ -15,7 +16,7 @@ export interface HttpRequest {
|
||||||
content?: string | ArrayBuffer;
|
content?: string | ArrayBuffer;
|
||||||
|
|
||||||
/** An object describing headers to apply to the request. */
|
/** An object describing headers to apply to the request. */
|
||||||
headers?: { [key: string]: string };
|
headers?: MessageHeaders;
|
||||||
|
|
||||||
/** The XMLHttpRequestResponseType to apply to the request. */
|
/** The XMLHttpRequestResponseType to apply to the request. */
|
||||||
responseType?: XMLHttpRequestResponseType;
|
responseType?: XMLHttpRequestResponseType;
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,7 @@ export class HttpConnection implements IConnection {
|
||||||
try {
|
try {
|
||||||
const response = await this.httpClient.post(negotiateUrl, {
|
const response = await this.httpClient.post(negotiateUrl, {
|
||||||
content: "",
|
content: "",
|
||||||
headers,
|
headers: { ...headers, ...this.options.headers },
|
||||||
withCredentials: this.options.withCredentials,
|
withCredentials: this.options.withCredentials,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -403,14 +403,14 @@ export class HttpConnection implements IConnection {
|
||||||
if (!this.options.WebSocket) {
|
if (!this.options.WebSocket) {
|
||||||
throw new Error("'WebSocket' is not supported in your environment.");
|
throw new Error("'WebSocket' is not supported in your environment.");
|
||||||
}
|
}
|
||||||
return new WebSocketTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.WebSocket);
|
return new WebSocketTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.WebSocket, this.options.headers || {});
|
||||||
case HttpTransportType.ServerSentEvents:
|
case HttpTransportType.ServerSentEvents:
|
||||||
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, this.options.withCredentials!);
|
return new ServerSentEventsTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.EventSource, this.options.withCredentials!, this.options.headers || {});
|
||||||
case HttpTransportType.LongPolling:
|
case HttpTransportType.LongPolling:
|
||||||
return new LongPollingTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.withCredentials!);
|
return new LongPollingTransport(this.httpClient, this.accessTokenFactory, this.logger, this.options.logMessageContent || false, this.options.withCredentials!, this.options.headers || {});
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown transport: ${transport}.`);
|
throw new Error(`Unknown transport: ${transport}.`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,16 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
import { HttpClient } from "./HttpClient";
|
import { HttpClient } from "./HttpClient";
|
||||||
|
import { MessageHeaders } from "./IHubProtocol";
|
||||||
import { ILogger, LogLevel } from "./ILogger";
|
import { ILogger, LogLevel } from "./ILogger";
|
||||||
import { HttpTransportType, ITransport } from "./ITransport";
|
import { HttpTransportType, ITransport } from "./ITransport";
|
||||||
import { EventSourceConstructor, WebSocketConstructor } from "./Polyfills";
|
import { EventSourceConstructor, WebSocketConstructor } from "./Polyfills";
|
||||||
|
|
||||||
/** Options provided to the 'withUrl' method on {@link @microsoft/signalr.HubConnectionBuilder} to configure options for the HTTP-based transports. */
|
/** Options provided to the 'withUrl' method on {@link @microsoft/signalr.HubConnectionBuilder} to configure options for the HTTP-based transports. */
|
||||||
export interface IHttpConnectionOptions {
|
export interface IHttpConnectionOptions {
|
||||||
|
/** {@link @microsoft/signalr.MessageHeaders} containing custom headers to be sent with every HTTP request. Note, setting headers in the browser will not work for WebSockets or the ServerSentEvents stream. */
|
||||||
|
headers?: MessageHeaders;
|
||||||
|
|
||||||
/** An {@link @microsoft/signalr.HttpClient} that will be used to make HTTP requests. */
|
/** An {@link @microsoft/signalr.HttpClient} that will be used to make HTTP requests. */
|
||||||
httpClient?: HttpClient;
|
httpClient?: HttpClient;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
import { AbortController } from "./AbortController";
|
import { AbortController } from "./AbortController";
|
||||||
import { HttpError, TimeoutError } from "./Errors";
|
import { HttpError, TimeoutError } from "./Errors";
|
||||||
import { HttpClient, HttpRequest } from "./HttpClient";
|
import { HttpClient, HttpRequest } from "./HttpClient";
|
||||||
|
import { MessageHeaders } from "./IHubProtocol";
|
||||||
import { ILogger, LogLevel } from "./ILogger";
|
import { ILogger, LogLevel } from "./ILogger";
|
||||||
import { ITransport, TransferFormat } from "./ITransport";
|
import { ITransport, TransferFormat } from "./ITransport";
|
||||||
import { Arg, getDataDetail, getUserAgentHeader, sendMessage } from "./Utils";
|
import { Arg, getDataDetail, getUserAgentHeader, sendMessage } from "./Utils";
|
||||||
|
|
@ -17,6 +18,7 @@ export class LongPollingTransport implements ITransport {
|
||||||
private readonly logMessageContent: boolean;
|
private readonly logMessageContent: boolean;
|
||||||
private readonly withCredentials: boolean;
|
private readonly withCredentials: boolean;
|
||||||
private readonly pollAbort: AbortController;
|
private readonly pollAbort: AbortController;
|
||||||
|
private readonly headers: MessageHeaders;
|
||||||
|
|
||||||
private url?: string;
|
private url?: string;
|
||||||
private running: boolean;
|
private running: boolean;
|
||||||
|
|
@ -31,13 +33,14 @@ 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, withCredentials: boolean) {
|
constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger, logMessageContent: boolean, withCredentials: boolean, headers: MessageHeaders) {
|
||||||
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.withCredentials = withCredentials;
|
||||||
|
this.headers = headers;
|
||||||
|
|
||||||
this.running = false;
|
this.running = false;
|
||||||
|
|
||||||
|
|
@ -60,9 +63,8 @@ export class LongPollingTransport implements ITransport {
|
||||||
throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");
|
throw new Error("Binary protocols over XmlHttpRequest not implementing advanced features are not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const headers = {};
|
|
||||||
const [name, value] = getUserAgentHeader();
|
const [name, value] = getUserAgentHeader();
|
||||||
headers[name] = value;
|
const headers = { [name]: value, ...this.headers };
|
||||||
|
|
||||||
const pollOptions: HttpRequest = {
|
const pollOptions: HttpRequest = {
|
||||||
abortSignal: this.pollAbort.signal,
|
abortSignal: this.pollAbort.signal,
|
||||||
|
|
@ -185,7 +187,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, this.withCredentials);
|
return sendMessage(this.logger, "LongPolling", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent, this.withCredentials, this.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async stop(): Promise<void> {
|
public async stop(): Promise<void> {
|
||||||
|
|
@ -206,7 +208,7 @@ export class LongPollingTransport implements ITransport {
|
||||||
headers[name] = value;
|
headers[name] = value;
|
||||||
|
|
||||||
const deleteOptions: HttpRequest = {
|
const deleteOptions: HttpRequest = {
|
||||||
headers,
|
headers: { ...headers, ...this.headers },
|
||||||
withCredentials: this.withCredentials,
|
withCredentials: this.withCredentials,
|
||||||
};
|
};
|
||||||
const token = await this.getAccessToken();
|
const token = await this.getAccessToken();
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
import { HttpClient } from "./HttpClient";
|
import { HttpClient } from "./HttpClient";
|
||||||
|
import { MessageHeaders } from "./IHubProtocol";
|
||||||
import { ILogger, LogLevel } from "./ILogger";
|
import { ILogger, LogLevel } from "./ILogger";
|
||||||
import { ITransport, TransferFormat } from "./ITransport";
|
import { ITransport, TransferFormat } from "./ITransport";
|
||||||
import { EventSourceConstructor } from "./Polyfills";
|
import { EventSourceConstructor } from "./Polyfills";
|
||||||
|
|
@ -17,18 +18,20 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
private readonly eventSourceConstructor: EventSourceConstructor;
|
private readonly eventSourceConstructor: EventSourceConstructor;
|
||||||
private eventSource?: EventSource;
|
private eventSource?: EventSource;
|
||||||
private url?: string;
|
private url?: string;
|
||||||
|
private headers: MessageHeaders;
|
||||||
|
|
||||||
public onreceive: ((data: string | ArrayBuffer) => void) | null;
|
public onreceive: ((data: string | ArrayBuffer) => void) | null;
|
||||||
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, withCredentials: boolean) {
|
logMessageContent: boolean, eventSourceConstructor: EventSourceConstructor, withCredentials: boolean, headers: MessageHeaders) {
|
||||||
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.withCredentials = withCredentials;
|
||||||
this.eventSourceConstructor = eventSourceConstructor;
|
this.eventSourceConstructor = eventSourceConstructor;
|
||||||
|
this.headers = headers;
|
||||||
|
|
||||||
this.onreceive = null;
|
this.onreceive = null;
|
||||||
this.onclose = null;
|
this.onclose = null;
|
||||||
|
|
@ -64,13 +67,12 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
} 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);
|
||||||
const headers = {
|
const headers: MessageHeaders = {};
|
||||||
Cookie: cookies,
|
headers.Cookie = cookies;
|
||||||
};
|
|
||||||
const [name, value] = getUserAgentHeader();
|
const [name, value] = getUserAgentHeader();
|
||||||
headers[name] = value;
|
headers[name] = value;
|
||||||
|
|
||||||
eventSource = new this.eventSourceConstructor(url, { withCredentials: this.withCredentials, headers } as EventSourceInit);
|
eventSource = new this.eventSourceConstructor(url, { withCredentials: this.withCredentials, headers: { ...headers, ...this.headers} } as EventSourceInit);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -112,7 +114,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, this.withCredentials);
|
return sendMessage(this.logger, "SSE", this.httpClient, this.url!, this.accessTokenFactory, data, this.logMessageContent, this.withCredentials, this.headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
public stop(): Promise<void> {
|
public stop(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
import { HttpClient } from "./HttpClient";
|
import { HttpClient } from "./HttpClient";
|
||||||
|
import { MessageHeaders } from "./IHubProtocol";
|
||||||
import { ILogger, LogLevel } from "./ILogger";
|
import { ILogger, LogLevel } from "./ILogger";
|
||||||
import { NullLogger } from "./Loggers";
|
import { NullLogger } from "./Loggers";
|
||||||
import { IStreamSubscriber, ISubscription } from "./Stream";
|
import { IStreamSubscriber, ISubscription } from "./Stream";
|
||||||
|
|
@ -85,7 +86,7 @@ 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,
|
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> {
|
content: string | ArrayBuffer, logMessageContent: boolean, withCredentials: boolean, defaultHeaders: MessageHeaders): Promise<void> {
|
||||||
let headers = {};
|
let headers = {};
|
||||||
if (accessTokenFactory) {
|
if (accessTokenFactory) {
|
||||||
const token = await accessTokenFactory();
|
const token = await accessTokenFactory();
|
||||||
|
|
@ -104,7 +105,7 @@ export async function sendMessage(logger: ILogger, transportName: string, httpCl
|
||||||
const responseType = isArrayBuffer(content) ? "arraybuffer" : "text";
|
const responseType = isArrayBuffer(content) ? "arraybuffer" : "text";
|
||||||
const response = await httpClient.post(url, {
|
const response = await httpClient.post(url, {
|
||||||
content,
|
content,
|
||||||
headers,
|
headers: { ...headers, ...defaultHeaders},
|
||||||
responseType,
|
responseType,
|
||||||
withCredentials,
|
withCredentials,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
import { HttpClient } from "./HttpClient";
|
import { HttpClient } from "./HttpClient";
|
||||||
|
import { MessageHeaders } from "./IHubProtocol";
|
||||||
import { ILogger, LogLevel } from "./ILogger";
|
import { ILogger, LogLevel } from "./ILogger";
|
||||||
import { ITransport, TransferFormat } from "./ITransport";
|
import { ITransport, TransferFormat } from "./ITransport";
|
||||||
import { WebSocketConstructor } from "./Polyfills";
|
import { WebSocketConstructor } from "./Polyfills";
|
||||||
|
|
@ -15,12 +16,13 @@ export class WebSocketTransport implements ITransport {
|
||||||
private readonly webSocketConstructor: WebSocketConstructor;
|
private readonly webSocketConstructor: WebSocketConstructor;
|
||||||
private readonly httpClient: HttpClient;
|
private readonly httpClient: HttpClient;
|
||||||
private webSocket?: WebSocket;
|
private webSocket?: WebSocket;
|
||||||
|
private headers: MessageHeaders;
|
||||||
|
|
||||||
public onreceive: ((data: string | ArrayBuffer) => void) | null;
|
public onreceive: ((data: string | ArrayBuffer) => void) | null;
|
||||||
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, webSocketConstructor: WebSocketConstructor) {
|
logMessageContent: boolean, webSocketConstructor: WebSocketConstructor, headers: MessageHeaders) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.accessTokenFactory = accessTokenFactory;
|
this.accessTokenFactory = accessTokenFactory;
|
||||||
this.logMessageContent = logMessageContent;
|
this.logMessageContent = logMessageContent;
|
||||||
|
|
@ -29,6 +31,7 @@ export class WebSocketTransport implements ITransport {
|
||||||
|
|
||||||
this.onreceive = null;
|
this.onreceive = null;
|
||||||
this.onclose = null;
|
this.onclose = null;
|
||||||
|
this.headers = headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async connect(url: string, transferFormat: TransferFormat): Promise<void> {
|
public async connect(url: string, transferFormat: TransferFormat): Promise<void> {
|
||||||
|
|
@ -59,9 +62,9 @@ export class WebSocketTransport implements ITransport {
|
||||||
headers[`Cookie`] = `${cookies}`;
|
headers[`Cookie`] = `${cookies}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only pass cookies when in non-browser environments
|
// Only pass headers when in non-browser environments
|
||||||
webSocket = new this.webSocketConstructor(url, undefined, {
|
webSocket = new this.webSocketConstructor(url, undefined, {
|
||||||
headers,
|
headers: { ...headers, ...this.headers },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,9 +44,11 @@ describe("HttpClient", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await testClient.get("http://localhost", {
|
await testClient.get("http://localhost", {
|
||||||
|
headers: { "X-HEADER": "VALUE"},
|
||||||
timeout: 42,
|
timeout: 42,
|
||||||
});
|
});
|
||||||
expect(request.timeout).toEqual(42);
|
expect(request.timeout).toEqual(42);
|
||||||
|
expect(request.headers).toEqual({ "X-HEADER": "VALUE"});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -86,9 +88,11 @@ describe("HttpClient", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
await testClient.post("http://localhost", {
|
await testClient.post("http://localhost", {
|
||||||
|
headers: { "X-HEADER": "VALUE"},
|
||||||
timeout: 42,
|
timeout: 42,
|
||||||
});
|
});
|
||||||
expect(request.timeout).toEqual(42);
|
expect(request.timeout).toEqual(42);
|
||||||
|
expect(request.headers).toEqual({ "X-HEADER": "VALUE"});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1152,6 +1152,30 @@ describe("HttpConnection", () => {
|
||||||
}, "Failed to start the connection: Error: nope");
|
}, "Failed to start the connection: Error: nope");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("overwrites library headers with user headers on negotiate", async () => {
|
||||||
|
await VerifyLogger.run(async (logger) => {
|
||||||
|
const headers = { "User-Agent": "Custom Agent", "X-HEADER": "VALUE" };
|
||||||
|
const options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
|
headers,
|
||||||
|
httpClient: new TestHttpClient()
|
||||||
|
.on("POST", (r) => {
|
||||||
|
expect(r.headers).toEqual(headers);
|
||||||
|
return new HttpResponse(200, "", "{\"error\":\"nope\"}");
|
||||||
|
}),
|
||||||
|
logger,
|
||||||
|
};
|
||||||
|
|
||||||
|
const connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
try {
|
||||||
|
await connection.start(TransferFormat.Text);
|
||||||
|
} catch {
|
||||||
|
} finally {
|
||||||
|
await connection.stop();
|
||||||
|
}
|
||||||
|
}, "Failed to start the connection: Error: nope");
|
||||||
|
});
|
||||||
|
|
||||||
it("logMessageContent displays correctly with binary data", async () => {
|
it("logMessageContent displays correctly with binary data", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
const availableTransport = { transport: "LongPolling", transferFormats: ["Text", "Binary"] };
|
const availableTransport = { transport: "LongPolling", transferFormats: ["Text", "Binary"] };
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ describe("LongPollingTransport", () => {
|
||||||
return new HttpResponse(200);
|
return new HttpResponse(200);
|
||||||
} else {
|
} else {
|
||||||
// Turn 'onabort' into a promise.
|
// Turn 'onabort' into a promise.
|
||||||
const abort = new Promise((resolve, reject) => {
|
const abort = new Promise((resolve) => {
|
||||||
if (r.abortSignal!.aborted) {
|
if (r.abortSignal!.aborted) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -40,7 +40,7 @@ describe("LongPollingTransport", () => {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("DELETE", () => new HttpResponse(202));
|
.on("DELETE", () => new HttpResponse(202));
|
||||||
const transport = new LongPollingTransport(client, undefined, logger, false, true);
|
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, true);
|
const transport = new LongPollingTransport(client, undefined, logger, false, true, {});
|
||||||
|
|
||||||
const stopPromise = makeClosedPromise(transport);
|
const stopPromise = makeClosedPromise(transport);
|
||||||
|
|
||||||
|
|
@ -82,7 +82,7 @@ describe("LongPollingTransport", () => {
|
||||||
const pollingPromiseSource = new PromiseSource();
|
const pollingPromiseSource = new PromiseSource();
|
||||||
const deleteSyncPoint = new SyncPoint();
|
const deleteSyncPoint = new SyncPoint();
|
||||||
const httpClient = new TestHttpClient()
|
const httpClient = new TestHttpClient()
|
||||||
.on("GET", async (r) => {
|
.on("GET", async () => {
|
||||||
if (firstPoll) {
|
if (firstPoll) {
|
||||||
firstPoll = false;
|
firstPoll = false;
|
||||||
return new HttpResponse(200);
|
return new HttpResponse(200);
|
||||||
|
|
@ -91,13 +91,13 @@ describe("LongPollingTransport", () => {
|
||||||
return new HttpResponse(204);
|
return new HttpResponse(204);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("DELETE", async (r) => {
|
.on("DELETE", async () => {
|
||||||
deleteSent = true;
|
deleteSent = true;
|
||||||
await deleteSyncPoint.waitToContinue();
|
await deleteSyncPoint.waitToContinue();
|
||||||
return new HttpResponse(202);
|
return new HttpResponse(202);
|
||||||
});
|
});
|
||||||
|
|
||||||
const transport = new LongPollingTransport(httpClient, undefined, logger, false, true);
|
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, true);
|
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);
|
||||||
|
|
||||||
|
|
@ -165,6 +165,67 @@ describe("LongPollingTransport", () => {
|
||||||
expect(secondPollUserAgent).toEqual(value);
|
expect(secondPollUserAgent).toEqual(value);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("overwrites library headers with user headers", async () => {
|
||||||
|
await VerifyLogger.run(async (logger) => {
|
||||||
|
const headers = { "User-Agent": "Custom Agent", "X-HEADER": "VALUE" };
|
||||||
|
let firstPoll = true;
|
||||||
|
let firstPollUserAgent = "";
|
||||||
|
let firstUserHeader = "";
|
||||||
|
let secondPollUserAgent = "";
|
||||||
|
let secondUserHeader = "";
|
||||||
|
let deleteUserAgent = "";
|
||||||
|
let deleteUserHeader = "";
|
||||||
|
const pollingPromiseSource = new PromiseSource();
|
||||||
|
const httpClient = new TestHttpClient()
|
||||||
|
.on("POST", async (r) => {
|
||||||
|
expect(r.content).toEqual({ message: "hello" });
|
||||||
|
expect(r.headers).toEqual(headers);
|
||||||
|
expect(r.method).toEqual("POST");
|
||||||
|
expect(r.url).toEqual("http://tempuri.org");
|
||||||
|
})
|
||||||
|
.on("GET", async (r) => {
|
||||||
|
if (firstPoll) {
|
||||||
|
firstPoll = false;
|
||||||
|
firstPollUserAgent = r.headers!["User-Agent"];
|
||||||
|
firstUserHeader = r.headers!["X-HEADER"];
|
||||||
|
return new HttpResponse(200);
|
||||||
|
} else {
|
||||||
|
secondPollUserAgent = r.headers!["User-Agent"];
|
||||||
|
secondUserHeader = r.headers!["X-HEADER"];
|
||||||
|
await pollingPromiseSource.promise;
|
||||||
|
return new HttpResponse(204);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.on("DELETE", async (r) => {
|
||||||
|
deleteUserAgent = r.headers!["User-Agent"];
|
||||||
|
deleteUserHeader = r.headers!["X-HEADER"];
|
||||||
|
return new HttpResponse(202);
|
||||||
|
});
|
||||||
|
|
||||||
|
const transport = new LongPollingTransport(httpClient, undefined, logger, false, true, headers);
|
||||||
|
|
||||||
|
await transport.connect("http://tempuri.org", TransferFormat.Text);
|
||||||
|
|
||||||
|
await transport.send({ message: "hello" });
|
||||||
|
|
||||||
|
// Begin stopping transport
|
||||||
|
const stopPromise = transport.stop();
|
||||||
|
|
||||||
|
// Allow polling to complete
|
||||||
|
pollingPromiseSource.resolve();
|
||||||
|
|
||||||
|
// Wait for stop to complete
|
||||||
|
await stopPromise;
|
||||||
|
|
||||||
|
expect(firstPollUserAgent).toEqual("Custom Agent");
|
||||||
|
expect(deleteUserAgent).toEqual("Custom Agent");
|
||||||
|
expect(secondPollUserAgent).toEqual("Custom Agent");
|
||||||
|
expect(firstUserHeader).toEqual("VALUE");
|
||||||
|
expect(secondUserHeader).toEqual("VALUE");
|
||||||
|
expect(deleteUserHeader).toEqual("VALUE");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function makeClosedPromise(transport: LongPollingTransport): Promise<void> {
|
function makeClosedPromise(transport: LongPollingTransport): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
import { MessageHeaders } from "../src/IHubProtocol";
|
||||||
import { TransferFormat } from "../src/ITransport";
|
import { TransferFormat } from "../src/ITransport";
|
||||||
|
|
||||||
import { HttpClient, HttpRequest } from "../src/HttpClient";
|
import { HttpClient, HttpRequest } from "../src/HttpClient";
|
||||||
|
|
@ -17,7 +18,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, true);
|
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 +28,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, true);
|
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 () => {
|
||||||
|
|
@ -48,7 +49,7 @@ describe("ServerSentEventsTransport", () => {
|
||||||
|
|
||||||
it("connect failure does not call onclose handler", async () => {
|
it("connect failure does not call onclose handler", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource, true);
|
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource, true, {});
|
||||||
let closeCalled = false;
|
let closeCalled = false;
|
||||||
sse.onclose = () => closeCalled = true;
|
sse.onclose = () => closeCalled = true;
|
||||||
|
|
||||||
|
|
@ -169,7 +170,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, true);
|
const sse = new ServerSentEventsTransport(new TestHttpClient(), undefined, logger, true, TestEventSource, true, {});
|
||||||
|
|
||||||
await expect(sse.send(""))
|
await expect(sse.send(""))
|
||||||
.rejects
|
.rejects
|
||||||
|
|
@ -221,10 +222,31 @@ describe("ServerSentEventsTransport", () => {
|
||||||
expect(request!.url).toBe("http://example.com");
|
expect(request!.url).toBe("http://example.com");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("overwrites library headers with user headers", async () => {
|
||||||
|
await VerifyLogger.run(async (logger) => {
|
||||||
|
let request: HttpRequest;
|
||||||
|
const httpClient = new TestHttpClient().on((r) => {
|
||||||
|
request = r;
|
||||||
|
return "";
|
||||||
|
});
|
||||||
|
|
||||||
|
const headers = { "User-Agent": "Custom Agent", "X-HEADER": "VALUE" };
|
||||||
|
const sse = await createAndStartSSE(logger, "http://example.com", undefined, httpClient, headers);
|
||||||
|
|
||||||
|
expect((TestEventSource.eventSource.eventSourceInitDict as any).headers["User-Agent"]).toEqual("Custom Agent");
|
||||||
|
expect((TestEventSource.eventSource.eventSourceInitDict as any).headers["X-HEADER"]).toEqual("VALUE");
|
||||||
|
await sse.send("");
|
||||||
|
|
||||||
|
expect(request!.headers!["User-Agent"]).toEqual("Custom Agent");
|
||||||
|
expect(request!.headers!["X-HEADER"]).toEqual("VALUE");
|
||||||
|
expect(request!.url).toBe("http://example.com");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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, headers?: MessageHeaders): Promise<ServerSentEventsTransport> {
|
||||||
const sse = new ServerSentEventsTransport(httpClient || new TestHttpClient(), accessTokenFactory, logger, true, TestEventSource, true);
|
const sse = new ServerSentEventsTransport(httpClient || new TestHttpClient(), accessTokenFactory, logger, true, TestEventSource, true, headers || {});
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
import { MessageHeaders } from "../src/IHubProtocol";
|
||||||
import { ILogger } from "../src/ILogger";
|
import { ILogger } from "../src/ILogger";
|
||||||
import { TransferFormat } from "../src/ITransport";
|
import { TransferFormat } from "../src/ITransport";
|
||||||
import { getUserAgentHeader } from "../src/Utils";
|
import { getUserAgentHeader } from "../src/Utils";
|
||||||
|
|
@ -24,7 +25,7 @@ describe("WebSocketTransport", () => {
|
||||||
|
|
||||||
it("connect waits for WebSocket to be connected", async () => {
|
it("connect waits for WebSocket to be connected", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket);
|
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket, {});
|
||||||
|
|
||||||
let connectComplete: boolean = false;
|
let connectComplete: boolean = false;
|
||||||
const connectPromise = (async () => {
|
const connectPromise = (async () => {
|
||||||
|
|
@ -46,7 +47,7 @@ describe("WebSocketTransport", () => {
|
||||||
it("connect fails if there is error during connect", async () => {
|
it("connect fails if there is error during connect", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
(global as any).ErrorEvent = TestErrorEvent;
|
(global as any).ErrorEvent = TestErrorEvent;
|
||||||
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket);
|
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket, {});
|
||||||
|
|
||||||
let connectComplete: boolean = false;
|
let connectComplete: boolean = false;
|
||||||
const connectPromise = (async () => {
|
const connectPromise = (async () => {
|
||||||
|
|
@ -70,7 +71,7 @@ describe("WebSocketTransport", () => {
|
||||||
it("connect failure does not call onclose handler", async () => {
|
it("connect failure does not call onclose handler", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
(global as any).ErrorEvent = TestErrorEvent;
|
(global as any).ErrorEvent = TestErrorEvent;
|
||||||
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket);
|
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket, {});
|
||||||
let closeCalled = false;
|
let closeCalled = false;
|
||||||
webSocket.onclose = () => closeCalled = true;
|
webSocket.onclose = () => closeCalled = true;
|
||||||
|
|
||||||
|
|
@ -257,6 +258,33 @@ describe("WebSocketTransport", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("overwrites library headers with user headers", async () => {
|
||||||
|
await VerifyLogger.run(async (logger) => {
|
||||||
|
(global as any).ErrorEvent = TestEvent;
|
||||||
|
const headers = { "User-Agent": "Custom Agent", "X-HEADER": "VALUE" };
|
||||||
|
const webSocket = await createAndStartWebSocket(logger, undefined, undefined, undefined, headers);
|
||||||
|
|
||||||
|
let closeCalled: boolean = false;
|
||||||
|
let error: Error;
|
||||||
|
webSocket.onclose = (e) => {
|
||||||
|
closeCalled = true;
|
||||||
|
error = e!;
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(TestWebSocket.webSocket.options!.headers[`User-Agent`]).toEqual("Custom Agent");
|
||||||
|
expect(TestWebSocket.webSocket.options!.headers[`X-HEADER`]).toEqual("VALUE");
|
||||||
|
|
||||||
|
await webSocket.stop();
|
||||||
|
|
||||||
|
expect(closeCalled).toBe(true);
|
||||||
|
expect(error!).toBeUndefined();
|
||||||
|
|
||||||
|
await expect(webSocket.send(""))
|
||||||
|
.rejects
|
||||||
|
.toBe("WebSocket is not in the OPEN state");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it("is closed from 'onreceive' callback throwing", async () => {
|
it("is closed from 'onreceive' callback throwing", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
(global as any).ErrorEvent = TestEvent;
|
(global as any).ErrorEvent = TestEvent;
|
||||||
|
|
@ -270,7 +298,7 @@ describe("WebSocketTransport", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const receiveError = new Error("callback error");
|
const receiveError = new Error("callback error");
|
||||||
webSocket.onreceive = (data) => {
|
webSocket.onreceive = () => {
|
||||||
throw receiveError;
|
throw receiveError;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -290,7 +318,7 @@ describe("WebSocketTransport", () => {
|
||||||
it("does not run onclose callback if Transport does not fully connect and exits", async () => {
|
it("does not run onclose callback if Transport does not fully connect and exits", async () => {
|
||||||
await VerifyLogger.run(async (logger) => {
|
await VerifyLogger.run(async (logger) => {
|
||||||
(global as any).ErrorEvent = TestErrorEvent;
|
(global as any).ErrorEvent = TestErrorEvent;
|
||||||
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket);
|
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket, {});
|
||||||
|
|
||||||
const connectPromise = webSocket.connect("http://example.com", TransferFormat.Text);
|
const connectPromise = webSocket.connect("http://example.com", TransferFormat.Text);
|
||||||
|
|
||||||
|
|
@ -318,8 +346,8 @@ describe("WebSocketTransport", () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function createAndStartWebSocket(logger: ILogger, url?: string, accessTokenFactory?: (() => string | Promise<string>), format?: TransferFormat): Promise<WebSocketTransport> {
|
async function createAndStartWebSocket(logger: ILogger, url?: string, accessTokenFactory?: (() => string | Promise<string>), format?: TransferFormat, headers?: MessageHeaders): Promise<WebSocketTransport> {
|
||||||
const webSocket = new WebSocketTransport(new TestHttpClient(), accessTokenFactory, logger, true, TestWebSocket);
|
const webSocket = new WebSocketTransport(new TestHttpClient(), accessTokenFactory, logger, true, TestWebSocket, headers || {});
|
||||||
|
|
||||||
const connectPromise = webSocket.connect(url || "http://example.com", format || TransferFormat.Text);
|
const connectPromise = webSocket.connect(url || "http://example.com", format || TransferFormat.Text);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue