This commit is contained in:
parent
4203540cb0
commit
2625b389b8
|
|
@ -58,12 +58,12 @@ namespace FunctionalTests
|
||||||
{
|
{
|
||||||
OnMessageReceived = context =>
|
OnMessageReceived = context =>
|
||||||
{
|
{
|
||||||
var signalRTokenHeader = context.Request.Query["signalRTokenHeader"];
|
var signalRTokenHeader = context.Request.Query["access_token"];
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(signalRTokenHeader) &&
|
if (!string.IsNullOrEmpty(signalRTokenHeader) &&
|
||||||
(context.HttpContext.WebSockets.IsWebSocketRequest || context.Request.Headers["Accept"] == "text/event-stream"))
|
(context.HttpContext.WebSockets.IsWebSocketRequest || context.Request.Headers["Accept"] == "text/event-stream"))
|
||||||
{
|
{
|
||||||
context.Token = context.Request.Query["signalRTokenHeader"];
|
context.Token = context.Request.Query["access_token"];
|
||||||
}
|
}
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -476,7 +476,7 @@ describe('hubConnection', function () {
|
||||||
hubConnection = new HubConnection('/authorizedhub', {
|
hubConnection = new HubConnection('/authorizedhub', {
|
||||||
transport: transportType,
|
transport: transportType,
|
||||||
logger: LogLevel.Trace,
|
logger: LogLevel.Trace,
|
||||||
accessToken: () => jwtToken
|
accessTokenFactory: () => jwtToken
|
||||||
});
|
});
|
||||||
hubConnection.onclose(function (error) {
|
hubConnection.onclose(function (error) {
|
||||||
expect(error).toBe(undefined);
|
expect(error).toBe(undefined);
|
||||||
|
|
|
||||||
|
|
@ -9,25 +9,29 @@ import { ITransport, TransportType, TransferMode } from "../src/Transports"
|
||||||
import { eachTransport, eachEndpointUrl } from "./Common";
|
import { eachTransport, eachEndpointUrl } from "./Common";
|
||||||
import { HttpResponse } from "../src/index";
|
import { HttpResponse } from "../src/index";
|
||||||
|
|
||||||
|
const commonOptions: IHttpConnectionOptions = {
|
||||||
|
logger: null
|
||||||
|
};
|
||||||
|
|
||||||
describe("HttpConnection", () => {
|
describe("HttpConnection", () => {
|
||||||
it("cannot be created with relative url if document object is not present", () => {
|
it("cannot be created with relative url if document object is not present", () => {
|
||||||
expect(() => new HttpConnection("/test"))
|
expect(() => new HttpConnection("/test", commonOptions))
|
||||||
.toThrow(new Error("Cannot resolve '/test'."));
|
.toThrow(new Error("Cannot resolve '/test'."));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("cannot be created with relative url if window object is not present", () => {
|
it("cannot be created with relative url if window object is not present", () => {
|
||||||
(<any>global).window = {};
|
(<any>global).window = {};
|
||||||
expect(() => new HttpConnection("/test"))
|
expect(() => new HttpConnection("/test", commonOptions))
|
||||||
.toThrow(new Error("Cannot resolve '/test'."));
|
.toThrow(new Error("Cannot resolve '/test'."));
|
||||||
delete (<any>global).window;
|
delete (<any>global).window;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("starting connection fails if getting id fails", async (done) => {
|
it("starting connection fails if getting id fails", async (done) => {
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => Promise.reject("error"))
|
.on("POST", r => Promise.reject("error"))
|
||||||
.on("GET", r => ""),
|
.on("GET", r => ""),
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
let connection = new HttpConnection("http://tempuri.org", options);
|
let connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
@ -45,6 +49,7 @@ describe("HttpConnection", () => {
|
||||||
|
|
||||||
it("cannot start a running connection", async (done) => {
|
it("cannot start a running connection", async (done) => {
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => {
|
.on("POST", r => {
|
||||||
connection.start()
|
connection.start()
|
||||||
|
|
@ -58,7 +63,6 @@ describe("HttpConnection", () => {
|
||||||
});
|
});
|
||||||
return Promise.reject("error");
|
return Promise.reject("error");
|
||||||
}),
|
}),
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
let connection = new HttpConnection("http://tempuri.org", options);
|
let connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
@ -75,13 +79,13 @@ describe("HttpConnection", () => {
|
||||||
it("can start a stopped connection", async (done) => {
|
it("can start a stopped connection", async (done) => {
|
||||||
let negotiateCalls = 0;
|
let negotiateCalls = 0;
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => {
|
.on("POST", r => {
|
||||||
negotiateCalls += 1;
|
negotiateCalls += 1;
|
||||||
return Promise.reject("reached negotiate");
|
return Promise.reject("reached negotiate");
|
||||||
})
|
})
|
||||||
.on("GET", r => ""),
|
.on("GET", r => ""),
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
let connection = new HttpConnection("http://tempuri.org", options);
|
let connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
@ -103,6 +107,7 @@ describe("HttpConnection", () => {
|
||||||
|
|
||||||
it("can stop a starting connection", async (done) => {
|
it("can stop a starting connection", async (done) => {
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => {
|
.on("POST", r => {
|
||||||
connection.stop();
|
connection.stop();
|
||||||
|
|
@ -112,7 +117,6 @@ describe("HttpConnection", () => {
|
||||||
connection.stop();
|
connection.stop();
|
||||||
return "";
|
return "";
|
||||||
}),
|
}),
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
let connection = new HttpConnection("http://tempuri.org", options);
|
let connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
@ -128,7 +132,7 @@ describe("HttpConnection", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("can stop a non-started connection", async (done) => {
|
it("can stop a non-started connection", async (done) => {
|
||||||
let connection = new HttpConnection("http://tempuri.org");
|
let connection = new HttpConnection("http://tempuri.org", commonOptions);
|
||||||
await connection.stop();
|
await connection.stop();
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
@ -151,11 +155,11 @@ describe("HttpConnection", () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => "{ \"connectionId\": \"42\" }")
|
.on("POST", r => "{ \"connectionId\": \"42\" }")
|
||||||
.on("GET", r => ""),
|
.on("GET", r => ""),
|
||||||
transport: fakeTransport,
|
transport: fakeTransport,
|
||||||
logger: null,
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -178,6 +182,7 @@ describe("HttpConnection", () => {
|
||||||
let negotiateUrl: string;
|
let negotiateUrl: string;
|
||||||
let connection: HttpConnection;
|
let connection: HttpConnection;
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => {
|
.on("POST", r => {
|
||||||
negotiateUrl = r.url;
|
negotiateUrl = r.url;
|
||||||
|
|
@ -188,7 +193,6 @@ describe("HttpConnection", () => {
|
||||||
connection.stop();
|
connection.stop();
|
||||||
return "";
|
return "";
|
||||||
}),
|
}),
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
connection = new HttpConnection(givenUrl, options);
|
connection = new HttpConnection(givenUrl, options);
|
||||||
|
|
@ -212,11 +216,11 @@ describe("HttpConnection", () => {
|
||||||
}
|
}
|
||||||
it(`cannot be started if requested ${TransportType[requestedTransport]} transport not available on server`, async done => {
|
it(`cannot be started if requested ${TransportType[requestedTransport]} transport not available on server`, async done => {
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||||
.on("GET", r => ""),
|
.on("GET", r => ""),
|
||||||
transport: requestedTransport,
|
transport: requestedTransport,
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
let connection = new HttpConnection("http://tempuri.org", options);
|
let connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
@ -234,10 +238,10 @@ describe("HttpConnection", () => {
|
||||||
|
|
||||||
it("cannot be started if no transport available on server and no transport requested", async done => {
|
it("cannot be started if no transport available on server and no transport requested", async done => {
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||||
.on("GET", r => ""),
|
.on("GET", r => ""),
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
let connection = new HttpConnection("http://tempuri.org", options);
|
let connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
@ -254,9 +258,9 @@ describe("HttpConnection", () => {
|
||||||
|
|
||||||
it('does not send negotiate request if WebSockets transport requested explicitly', async done => {
|
it('does not send negotiate request if WebSockets transport requested explicitly', async done => {
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient(),
|
httpClient: new TestHttpClient(),
|
||||||
transport: TransportType.WebSockets,
|
transport: TransportType.WebSockets,
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
let connection = new HttpConnection("http://tempuri.org", options);
|
let connection = new HttpConnection("http://tempuri.org", options);
|
||||||
|
|
@ -291,11 +295,11 @@ describe("HttpConnection", () => {
|
||||||
} as ITransport;
|
} as ITransport;
|
||||||
|
|
||||||
let options: IHttpConnectionOptions = {
|
let options: IHttpConnectionOptions = {
|
||||||
|
...commonOptions,
|
||||||
httpClient: new TestHttpClient()
|
httpClient: new TestHttpClient()
|
||||||
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
.on("POST", r => "{ \"connectionId\": \"42\", \"availableTransports\": [] }")
|
||||||
.on("GET", r => ""),
|
.on("GET", r => ""),
|
||||||
transport: fakeTransport,
|
transport: fakeTransport,
|
||||||
logger: null
|
|
||||||
} as IHttpConnectionOptions;
|
} as IHttpConnectionOptions;
|
||||||
|
|
||||||
let connection = new HttpConnection("https://tempuri.org", options);
|
let connection = new HttpConnection("https://tempuri.org", options);
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,16 @@ import { MessageType } from "../src/IHubProtocol"
|
||||||
import { asyncit as it, captureException, delay, PromiseSource } from './Utils';
|
import { asyncit as it, captureException, delay, PromiseSource } from './Utils';
|
||||||
import { IHubConnectionOptions } from "../src/HubConnection";
|
import { IHubConnectionOptions } from "../src/HubConnection";
|
||||||
|
|
||||||
|
const commonOptions: IHubConnectionOptions = {
|
||||||
|
logger: null,
|
||||||
|
}
|
||||||
|
|
||||||
describe("HubConnection", () => {
|
describe("HubConnection", () => {
|
||||||
|
|
||||||
describe("start", () => {
|
describe("start", () => {
|
||||||
it("sends negotiation message", async () => {
|
it("sends negotiation message", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
await hubConnection.start();
|
await hubConnection.start();
|
||||||
expect(connection.sentData.length).toBe(1)
|
expect(connection.sentData.length).toBe(1)
|
||||||
expect(JSON.parse(connection.sentData[0])).toEqual({
|
expect(JSON.parse(connection.sentData[0])).toEqual({
|
||||||
|
|
@ -32,7 +36,7 @@ describe("HubConnection", () => {
|
||||||
it("sends a non blocking invocation", async () => {
|
it("sends a non blocking invocation", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invokePromise = hubConnection.send("testMethod", "arg", 42)
|
let invokePromise = hubConnection.send("testMethod", "arg", 42)
|
||||||
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
|
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
|
||||||
|
|
||||||
|
|
@ -56,7 +60,7 @@ describe("HubConnection", () => {
|
||||||
it("sends an invocation", async () => {
|
it("sends an invocation", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invokePromise = hubConnection.invoke("testMethod", "arg", 42)
|
let invokePromise = hubConnection.invoke("testMethod", "arg", 42)
|
||||||
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
|
.catch((_) => { }); // Suppress exception and unhandled promise rejection warning.
|
||||||
|
|
||||||
|
|
@ -79,7 +83,7 @@ describe("HubConnection", () => {
|
||||||
it("rejects the promise when an error is received", async () => {
|
it("rejects the promise when an error is received", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||||
|
|
||||||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, error: "foo" });
|
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, error: "foo" });
|
||||||
|
|
@ -91,7 +95,7 @@ describe("HubConnection", () => {
|
||||||
it("resolves the promise when a result is received", async () => {
|
it("resolves the promise when a result is received", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||||
|
|
||||||
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, result: "foo" });
|
connection.receive({ type: MessageType.Completion, invocationId: connection.lastInvocationId, result: "foo" });
|
||||||
|
|
@ -102,7 +106,7 @@ describe("HubConnection", () => {
|
||||||
it("completes pending invocations when stopped", async () => {
|
it("completes pending invocations when stopped", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invokePromise = hubConnection.invoke("testMethod");
|
let invokePromise = hubConnection.invoke("testMethod");
|
||||||
hubConnection.stop();
|
hubConnection.stop();
|
||||||
|
|
||||||
|
|
@ -113,7 +117,7 @@ describe("HubConnection", () => {
|
||||||
it("completes pending invocations when connection is lost", async () => {
|
it("completes pending invocations when connection is lost", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invokePromise = hubConnection.invoke("testMethod");
|
let invokePromise = hubConnection.invoke("testMethod");
|
||||||
// Typically this would be called by the transport
|
// Typically this would be called by the transport
|
||||||
connection.onclose(new Error("Connection lost"));
|
connection.onclose(new Error("Connection lost"));
|
||||||
|
|
@ -149,7 +153,7 @@ describe("HubConnection", () => {
|
||||||
|
|
||||||
it("callback invoked when servers invokes a method on the client", async () => {
|
it("callback invoked when servers invokes a method on the client", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let value = "";
|
let value = "";
|
||||||
hubConnection.on("message", v => value = v);
|
hubConnection.on("message", v => value = v);
|
||||||
|
|
||||||
|
|
@ -166,7 +170,7 @@ describe("HubConnection", () => {
|
||||||
|
|
||||||
it("can have multiple callbacks", async () => {
|
it("can have multiple callbacks", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let numInvocations1 = 0;
|
let numInvocations1 = 0;
|
||||||
let numInvocations2 = 0;
|
let numInvocations2 = 0;
|
||||||
hubConnection.on("message", () => numInvocations1++);
|
hubConnection.on("message", () => numInvocations1++);
|
||||||
|
|
@ -186,7 +190,7 @@ describe("HubConnection", () => {
|
||||||
|
|
||||||
it("can unsubscribe from on", async () => {
|
it("can unsubscribe from on", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
|
|
||||||
var numInvocations = 0;
|
var numInvocations = 0;
|
||||||
var callback = () => numInvocations++;
|
var callback = () => numInvocations++;
|
||||||
|
|
@ -215,7 +219,7 @@ describe("HubConnection", () => {
|
||||||
|
|
||||||
it("unsubscribing from non-existing callbacks no-ops", async () => {
|
it("unsubscribing from non-existing callbacks no-ops", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
|
|
||||||
hubConnection.off("_", () => { });
|
hubConnection.off("_", () => { });
|
||||||
hubConnection.on("message", t => { });
|
hubConnection.on("message", t => { });
|
||||||
|
|
@ -267,7 +271,7 @@ describe("HubConnection", () => {
|
||||||
it("sends an invocation", async () => {
|
it("sends an invocation", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invokePromise = hubConnection.stream("testStream", "arg", 42);
|
let invokePromise = hubConnection.stream("testStream", "arg", 42);
|
||||||
|
|
||||||
// Verify the message is sent
|
// Verify the message is sent
|
||||||
|
|
@ -289,7 +293,7 @@ describe("HubConnection", () => {
|
||||||
it("completes with an error when an error is yielded", async () => {
|
it("completes with an error when an error is yielded", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let observer = new TestObserver();
|
let observer = new TestObserver();
|
||||||
hubConnection.stream<any>("testMethod", "arg", 42)
|
hubConnection.stream<any>("testMethod", "arg", 42)
|
||||||
.subscribe(observer);
|
.subscribe(observer);
|
||||||
|
|
@ -303,7 +307,7 @@ describe("HubConnection", () => {
|
||||||
it("completes the observer when a completion is received", async () => {
|
it("completes the observer when a completion is received", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let observer = new TestObserver();
|
let observer = new TestObserver();
|
||||||
hubConnection.stream<any>("testMethod", "arg", 42)
|
hubConnection.stream<any>("testMethod", "arg", 42)
|
||||||
.subscribe(observer);
|
.subscribe(observer);
|
||||||
|
|
@ -316,7 +320,7 @@ describe("HubConnection", () => {
|
||||||
it("completes pending streams when stopped", async () => {
|
it("completes pending streams when stopped", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let observer = new TestObserver();
|
let observer = new TestObserver();
|
||||||
hubConnection.stream<any>("testMethod")
|
hubConnection.stream<any>("testMethod")
|
||||||
.subscribe(observer);
|
.subscribe(observer);
|
||||||
|
|
@ -329,7 +333,7 @@ describe("HubConnection", () => {
|
||||||
it("completes pending streams when connection is lost", async () => {
|
it("completes pending streams when connection is lost", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let observer = new TestObserver();
|
let observer = new TestObserver();
|
||||||
hubConnection.stream<any>("testMethod")
|
hubConnection.stream<any>("testMethod")
|
||||||
.subscribe(observer);
|
.subscribe(observer);
|
||||||
|
|
@ -344,7 +348,7 @@ describe("HubConnection", () => {
|
||||||
it("yields items as they arrive", async () => {
|
it("yields items as they arrive", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let observer = new TestObserver();
|
let observer = new TestObserver();
|
||||||
hubConnection.stream<any>("testMethod")
|
hubConnection.stream<any>("testMethod")
|
||||||
.subscribe(observer);
|
.subscribe(observer);
|
||||||
|
|
@ -365,7 +369,7 @@ describe("HubConnection", () => {
|
||||||
it("does not require error function registered", async () => {
|
it("does not require error function registered", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let observer = hubConnection.stream("testMethod").subscribe({
|
let observer = hubConnection.stream("testMethod").subscribe({
|
||||||
next: val => { }
|
next: val => { }
|
||||||
});
|
});
|
||||||
|
|
@ -378,7 +382,7 @@ describe("HubConnection", () => {
|
||||||
it("does not require complete function registered", async () => {
|
it("does not require complete function registered", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let observer = hubConnection.stream("testMethod").subscribe({
|
let observer = hubConnection.stream("testMethod").subscribe({
|
||||||
next: val => { }
|
next: val => { }
|
||||||
});
|
});
|
||||||
|
|
@ -391,7 +395,7 @@ describe("HubConnection", () => {
|
||||||
it("can be canceled", () => {
|
it("can be canceled", () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
|
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let observer = new TestObserver();
|
let observer = new TestObserver();
|
||||||
let subscription = hubConnection.stream("testMethod")
|
let subscription = hubConnection.stream("testMethod")
|
||||||
.subscribe(observer);
|
.subscribe(observer);
|
||||||
|
|
@ -417,7 +421,7 @@ describe("HubConnection", () => {
|
||||||
describe("onClose", () => {
|
describe("onClose", () => {
|
||||||
it("can have multiple callbacks", async () => {
|
it("can have multiple callbacks", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invocations = 0;
|
let invocations = 0;
|
||||||
hubConnection.onclose(e => invocations++);
|
hubConnection.onclose(e => invocations++);
|
||||||
hubConnection.onclose(e => invocations++);
|
hubConnection.onclose(e => invocations++);
|
||||||
|
|
@ -428,7 +432,7 @@ describe("HubConnection", () => {
|
||||||
|
|
||||||
it("callbacks receive error", async () => {
|
it("callbacks receive error", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let error: Error;
|
let error: Error;
|
||||||
hubConnection.onclose(e => error = e);
|
hubConnection.onclose(e => error = e);
|
||||||
|
|
||||||
|
|
@ -439,7 +443,7 @@ describe("HubConnection", () => {
|
||||||
|
|
||||||
it("ignores null callbacks", async () => {
|
it("ignores null callbacks", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
hubConnection.onclose(null);
|
hubConnection.onclose(null);
|
||||||
hubConnection.onclose(undefined);
|
hubConnection.onclose(undefined);
|
||||||
// Typically this would be called by the transport
|
// Typically this would be called by the transport
|
||||||
|
|
@ -453,7 +457,7 @@ describe("HubConnection", () => {
|
||||||
// Receive the ping mid-invocation so we can see that the rest of the flow works fine
|
// Receive the ping mid-invocation so we can see that the rest of the flow works fine
|
||||||
|
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { logger: null });
|
let hubConnection = new HubConnection(connection, commonOptions);
|
||||||
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
let invokePromise = hubConnection.invoke("testMethod", "arg", 42);
|
||||||
|
|
||||||
connection.receive({ type: MessageType.Ping });
|
connection.receive({ type: MessageType.Ping });
|
||||||
|
|
@ -464,7 +468,7 @@ describe("HubConnection", () => {
|
||||||
|
|
||||||
it("does not terminate if messages are received", async () => {
|
it("does not terminate if messages are received", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { timeoutInMilliseconds: 100, logger: null });
|
let hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 });
|
||||||
|
|
||||||
let p = new PromiseSource<Error>();
|
let p = new PromiseSource<Error>();
|
||||||
hubConnection.onclose(error => p.resolve(error));
|
hubConnection.onclose(error => p.resolve(error));
|
||||||
|
|
@ -489,7 +493,7 @@ describe("HubConnection", () => {
|
||||||
|
|
||||||
it("terminates if no messages received within timeout interval", async () => {
|
it("terminates if no messages received within timeout interval", async () => {
|
||||||
let connection = new TestConnection();
|
let connection = new TestConnection();
|
||||||
let hubConnection = new HubConnection(connection, { timeoutInMilliseconds: 100, logger: null });
|
let hubConnection = new HubConnection(connection, { ...commonOptions, timeoutInMilliseconds: 100 });
|
||||||
|
|
||||||
let p = new PromiseSource<Error>();
|
let p = new PromiseSource<Error>();
|
||||||
hubConnection.onclose(error => p.resolve(error));
|
hubConnection.onclose(error => p.resolve(error));
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ export interface IHttpConnectionOptions {
|
||||||
httpClient?: HttpClient;
|
httpClient?: HttpClient;
|
||||||
transport?: TransportType | ITransport;
|
transport?: TransportType | ITransport;
|
||||||
logger?: ILogger | LogLevel;
|
logger?: ILogger | LogLevel;
|
||||||
accessToken?: () => string;
|
accessTokenFactory?: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum ConnectionState {
|
const enum ConnectionState {
|
||||||
|
|
@ -42,7 +42,10 @@ export class HttpConnection implements IConnection {
|
||||||
constructor(url: string, options: IHttpConnectionOptions = {}) {
|
constructor(url: string, options: IHttpConnectionOptions = {}) {
|
||||||
this.logger = LoggerFactory.createLogger(options.logger);
|
this.logger = LoggerFactory.createLogger(options.logger);
|
||||||
this.baseUrl = this.resolveUrl(url);
|
this.baseUrl = this.resolveUrl(url);
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
options.accessTokenFactory = options.accessTokenFactory || (() => null);
|
||||||
|
|
||||||
this.httpClient = options.httpClient || new DefaultHttpClient();
|
this.httpClient = options.httpClient || new DefaultHttpClient();
|
||||||
this.connectionState = ConnectionState.Disconnected;
|
this.connectionState = ConnectionState.Disconnected;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
@ -68,9 +71,10 @@ export class HttpConnection implements IConnection {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let headers;
|
let headers;
|
||||||
if (this.options.accessToken) {
|
let token = this.options.accessTokenFactory();
|
||||||
|
if (token) {
|
||||||
headers = new Map<string, string>();
|
headers = new Map<string, string>();
|
||||||
headers.set("Authorization", `Bearer ${this.options.accessToken()}`);
|
headers.set("Authorization", `Bearer ${token}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
let negotiatePayload = await this.httpClient.post(this.resolveNegotiateUrl(this.baseUrl), {
|
let negotiatePayload = await this.httpClient.post(this.resolveNegotiateUrl(this.baseUrl), {
|
||||||
|
|
@ -119,13 +123,13 @@ export class HttpConnection implements IConnection {
|
||||||
transport = TransportType[availableTransports[0]];
|
transport = TransportType[availableTransports[0]];
|
||||||
}
|
}
|
||||||
if (transport === TransportType.WebSockets && availableTransports.indexOf(TransportType[transport]) >= 0) {
|
if (transport === TransportType.WebSockets && availableTransports.indexOf(TransportType[transport]) >= 0) {
|
||||||
return new WebSocketTransport(this.options.accessToken, this.logger);
|
return new WebSocketTransport(this.options.accessTokenFactory, this.logger);
|
||||||
}
|
}
|
||||||
if (transport === TransportType.ServerSentEvents && availableTransports.indexOf(TransportType[transport]) >= 0) {
|
if (transport === TransportType.ServerSentEvents && availableTransports.indexOf(TransportType[transport]) >= 0) {
|
||||||
return new ServerSentEventsTransport(this.httpClient, this.options.accessToken, this.logger);
|
return new ServerSentEventsTransport(this.httpClient, this.options.accessTokenFactory, this.logger);
|
||||||
}
|
}
|
||||||
if (transport === TransportType.LongPolling && availableTransports.indexOf(TransportType[transport]) >= 0) {
|
if (transport === TransportType.LongPolling && availableTransports.indexOf(TransportType[transport]) >= 0) {
|
||||||
return new LongPollingTransport(this.httpClient, this.options.accessToken, this.logger);
|
return new LongPollingTransport(this.httpClient, this.options.accessTokenFactory, this.logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.isITransport(transport)) {
|
if (this.isITransport(transport)) {
|
||||||
|
|
|
||||||
|
|
@ -29,21 +29,21 @@ export interface ITransport {
|
||||||
|
|
||||||
export class WebSocketTransport implements ITransport {
|
export class WebSocketTransport implements ITransport {
|
||||||
private readonly logger: ILogger;
|
private readonly logger: ILogger;
|
||||||
private readonly accessToken: () => string;
|
private readonly accessTokenFactory: () => string;
|
||||||
private webSocket: WebSocket;
|
private webSocket: WebSocket;
|
||||||
|
|
||||||
constructor(accessToken: () => string, logger: ILogger) {
|
constructor(accessTokenFactory: () => string, logger: ILogger) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.accessToken = accessToken;
|
this.accessTokenFactory = accessTokenFactory || (() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(url: string, requestedTransferMode: TransferMode, connection: IConnection): Promise<TransferMode> {
|
connect(url: string, requestedTransferMode: TransferMode, connection: IConnection): Promise<TransferMode> {
|
||||||
|
|
||||||
return new Promise<TransferMode>((resolve, reject) => {
|
return new Promise<TransferMode>((resolve, reject) => {
|
||||||
url = url.replace(/^http/, "ws");
|
url = url.replace(/^http/, "ws");
|
||||||
if (this.accessToken) {
|
let token = this.accessTokenFactory();
|
||||||
let token = this.accessToken();
|
if (token) {
|
||||||
url += (url.indexOf("?") < 0 ? "?" : "&") + `signalRTokenHeader=${token}`;
|
url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let webSocket = new WebSocket(url);
|
let webSocket = new WebSocket(url);
|
||||||
|
|
@ -105,14 +105,14 @@ export class WebSocketTransport implements ITransport {
|
||||||
|
|
||||||
export class ServerSentEventsTransport implements ITransport {
|
export class ServerSentEventsTransport implements ITransport {
|
||||||
private readonly httpClient: HttpClient;
|
private readonly httpClient: HttpClient;
|
||||||
private readonly accessToken: () => string;
|
private readonly accessTokenFactory: () => string;
|
||||||
private readonly logger: ILogger;
|
private readonly logger: ILogger;
|
||||||
private eventSource: EventSource;
|
private eventSource: EventSource;
|
||||||
private url: string;
|
private url: string;
|
||||||
|
|
||||||
constructor(httpClient: HttpClient, accessToken: () => string, logger: ILogger) {
|
constructor(httpClient: HttpClient, accessTokenFactory: () => string, logger: ILogger) {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.accessToken = accessToken;
|
this.accessTokenFactory = accessTokenFactory || (() => null);
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,9 +123,9 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
|
|
||||||
this.url = url;
|
this.url = url;
|
||||||
return new Promise<TransferMode>((resolve, reject) => {
|
return new Promise<TransferMode>((resolve, reject) => {
|
||||||
if (this.accessToken) {
|
let token = this.accessTokenFactory();
|
||||||
let token = this.accessToken();
|
if (token) {
|
||||||
url += (url.indexOf("?") < 0 ? "?" : "&") + `signalRTokenHeader=${token}`;
|
url += (url.indexOf("?") < 0 ? "?" : "&") + `access_token=${encodeURIComponent(token)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
let eventSource = new EventSource(url);
|
let eventSource = new EventSource(url);
|
||||||
|
|
@ -145,7 +145,7 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
eventSource.onerror = (e: ErrorEvent) => {
|
eventSource.onerror = (e: any) => {
|
||||||
reject();
|
reject();
|
||||||
|
|
||||||
// don't report an error if the transport did not start successfully
|
// don't report an error if the transport did not start successfully
|
||||||
|
|
@ -168,7 +168,7 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(data: any): Promise<void> {
|
async send(data: any): Promise<void> {
|
||||||
return send(this.httpClient, this.url, this.accessToken, data);
|
return send(this.httpClient, this.url, this.accessTokenFactory, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): Promise<void> {
|
stop(): Promise<void> {
|
||||||
|
|
@ -185,16 +185,16 @@ export class ServerSentEventsTransport implements ITransport {
|
||||||
|
|
||||||
export class LongPollingTransport implements ITransport {
|
export class LongPollingTransport implements ITransport {
|
||||||
private readonly httpClient: HttpClient;
|
private readonly httpClient: HttpClient;
|
||||||
private readonly accessToken: () => string;
|
private readonly accessTokenFactory: () => string;
|
||||||
private readonly logger: ILogger;
|
private readonly logger: ILogger;
|
||||||
|
|
||||||
private url: string;
|
private url: string;
|
||||||
private pollXhr: XMLHttpRequest;
|
private pollXhr: XMLHttpRequest;
|
||||||
private pollAbort: AbortController;
|
private pollAbort: AbortController;
|
||||||
|
|
||||||
constructor(httpClient: HttpClient, accessToken: () => string, logger: ILogger) {
|
constructor(httpClient: HttpClient, accessTokenFactory: () => string, logger: ILogger) {
|
||||||
this.httpClient = httpClient;
|
this.httpClient = httpClient;
|
||||||
this.accessToken = accessToken;
|
this.accessTokenFactory = accessTokenFactory || (() => null);
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.pollAbort = new AbortController();
|
this.pollAbort = new AbortController();
|
||||||
}
|
}
|
||||||
|
|
@ -225,8 +225,9 @@ export class LongPollingTransport implements ITransport {
|
||||||
pollOptions.responseType = "arraybuffer";
|
pollOptions.responseType = "arraybuffer";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.accessToken) {
|
let token = this.accessTokenFactory();
|
||||||
pollOptions.headers.set("Authorization", `Bearer ${this.accessToken()}`);
|
if (token) {
|
||||||
|
pollOptions.headers.set("Authorization", `Bearer ${token}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!this.pollAbort.signal.aborted) {
|
while (!this.pollAbort.signal.aborted) {
|
||||||
|
|
@ -281,7 +282,7 @@ export class LongPollingTransport implements ITransport {
|
||||||
}
|
}
|
||||||
|
|
||||||
async send(data: any): Promise<void> {
|
async send(data: any): Promise<void> {
|
||||||
return send(this.httpClient, this.url, this.accessToken, data);
|
return send(this.httpClient, this.url, this.accessTokenFactory, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): Promise<void> {
|
stop(): Promise<void> {
|
||||||
|
|
@ -293,11 +294,12 @@ export class LongPollingTransport implements ITransport {
|
||||||
onclose: TransportClosed;
|
onclose: TransportClosed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function send(httpClient: HttpClient, url: string, accessToken: () => string, content: string | ArrayBuffer): Promise<void> {
|
async function send(httpClient: HttpClient, url: string, accessTokenFactory: () => string, content: string | ArrayBuffer): Promise<void> {
|
||||||
let headers;
|
let headers;
|
||||||
if (accessToken) {
|
let token = accessTokenFactory();
|
||||||
|
if (token) {
|
||||||
headers = new Map<string, string>();
|
headers = new Map<string, string>();
|
||||||
headers.set("Authorization", `Bearer ${accessToken()}`)
|
headers.set("Authorization", `Bearer ${accessTokenFactory()}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
await httpClient.post(url, {
|
await httpClient.post(url, {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
// 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.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
@ -50,12 +50,12 @@ namespace JwtSample
|
||||||
{
|
{
|
||||||
OnMessageReceived = context =>
|
OnMessageReceived = context =>
|
||||||
{
|
{
|
||||||
var signalRTokenHeader = context.Request.Query["signalRTokenHeader"];
|
var accessToken = context.Request.Query["access_token"];
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(signalRTokenHeader) &&
|
if (!string.IsNullOrEmpty(accessToken) &&
|
||||||
(context.HttpContext.WebSockets.IsWebSocketRequest || context.Request.Headers["Accept"] == "text/event-stream"))
|
(context.HttpContext.WebSockets.IsWebSocketRequest || context.Request.Headers["Accept"] == "text/event-stream"))
|
||||||
{
|
{
|
||||||
context.Token = context.Request.Query["signalRTokenHeader"];
|
context.Token = context.Request.Query["access_token"];
|
||||||
}
|
}
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@
|
||||||
.then(function () {
|
.then(function () {
|
||||||
var options = {
|
var options = {
|
||||||
transport: transportType,
|
transport: transportType,
|
||||||
accessToken: function () { return tokens[clientId]; }
|
accessTokenFactory: function () { return tokens[clientId]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
connection = new signalR.HubConnection('/broadcast', options);
|
connection = new signalR.HubConnection('/broadcast', options);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue