Skip to content

[TS] Add support for negotiateVersion and connectionToken #14157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 29 additions & 12 deletions src/SignalR/clients/ts/signalr/src/HttpConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const enum ConnectionState {
/** @private */
export interface INegotiateResponse {
connectionId?: string;
connectionToken?: string;
negotiateVersion?: number;
availableTransports?: IAvailableTransport[];
url?: string;
accessToken?: string;
Expand Down Expand Up @@ -70,6 +72,8 @@ export class HttpConnection implements IConnection {
public onreceive: ((data: string | ArrayBuffer) => void) | null;
public onclose: ((e?: Error) => void) | null;

private readonly negotiateVersion: number = 1;

constructor(url: string, options: IHttpConnectionOptions = {}) {
Arg.isRequired(url, "url");

Expand Down Expand Up @@ -272,8 +276,6 @@ export class HttpConnection implements IConnection {
throw new Error("Negotiate redirection limit exceeded.");
}

this.connectionId = negotiateResponse.connectionId;

await this.createTransport(url, this.options.transport, negotiateResponse, transferFormat);
}

Expand Down Expand Up @@ -322,53 +324,63 @@ export class HttpConnection implements IConnection {
return Promise.reject(new Error(`Unexpected status code returned from negotiate ${response.statusCode}`));
}

return JSON.parse(response.content as string) as INegotiateResponse;
const negotiateResponse = JSON.parse(response.content as string) as INegotiateResponse;
if (!negotiateResponse.negotiateVersion || negotiateResponse.negotiateVersion < 1) {
// Negotiate version 0 doesn't use connectionToken
// So we set it equal to connectionId so all our logic can use connectionToken without being aware of the negotiate version
negotiateResponse.connectionToken = negotiateResponse.connectionId;
}
return negotiateResponse;
} catch (e) {
this.logger.log(LogLevel.Error, "Failed to complete negotiation with the server: " + e);
return Promise.reject(e);
}
}

private createConnectUrl(url: string, connectionId: string | null | undefined) {
if (!connectionId) {
private createConnectUrl(url: string, connectionToken: string | null | undefined) {
if (!connectionToken) {
return url;
}
return url + (url.indexOf("?") === -1 ? "?" : "&") + `id=${connectionId}`;

return url + (url.indexOf("?") === -1 ? "?" : "&") + `id=${connectionToken}`;
}

private async createTransport(url: string, requestedTransport: HttpTransportType | ITransport | undefined, negotiateResponse: INegotiateResponse, requestedTransferFormat: TransferFormat): Promise<void> {
let connectUrl = this.createConnectUrl(url, negotiateResponse.connectionId);
let connectUrl = this.createConnectUrl(url, negotiateResponse.connectionToken);
if (this.isITransport(requestedTransport)) {
this.logger.log(LogLevel.Debug, "Connection was provided an instance of ITransport, using that directly.");
this.transport = requestedTransport;
await this.startTransport(connectUrl, requestedTransferFormat);

this.connectionId = negotiateResponse.connectionId;
return;
}

const transportExceptions: any[] = [];
const transports = negotiateResponse.availableTransports || [];
let negotiate: INegotiateResponse | undefined = negotiateResponse;
for (const endpoint of transports) {
const transportOrError = this.resolveTransportOrError(endpoint, requestedTransport, requestedTransferFormat);
if (transportOrError instanceof Error) {
// Store the error and continue, we don't want to cause a re-negotiate in these cases
transportExceptions.push(`${endpoint.transport} failed: ${transportOrError}`);
} else if (this.isITransport(transportOrError)) {
this.transport = transportOrError;
if (!negotiateResponse.connectionId) {
if (!negotiate) {
try {
negotiateResponse = await this.getNegotiationResponse(url);
negotiate = await this.getNegotiationResponse(url);
} catch (ex) {
return Promise.reject(ex);
}
connectUrl = this.createConnectUrl(url, negotiateResponse.connectionId);
connectUrl = this.createConnectUrl(url, negotiate.connectionToken);
}
try {
await this.startTransport(connectUrl, requestedTransferFormat);
this.connectionId = negotiate.connectionId;
return;
} catch (ex) {
this.logger.log(LogLevel.Error, `Failed to start the transport '${endpoint.transport}': ${ex}`);
negotiateResponse.connectionId = undefined;
negotiate = undefined;
transportExceptions.push(`${endpoint.transport} failed: ${ex}`);

if (this.connectionState !== ConnectionState.Connecting) {
Expand Down Expand Up @@ -504,7 +516,7 @@ export class HttpConnection implements IConnection {

// Setting the url to the href propery of an anchor tag handles normalization
// for us. There are 3 main cases.
// 1. Relative path normalization e.g "b" -> "http://localhost:5000/a/b"
// 1. Relative path normalization e.g "b" -> "http://localhost:5000/a/b"
// 2. Absolute path normalization e.g "/a/b" -> "http://localhost:5000/a/b"
// 3. Networkpath reference normalization e.g "//localhost:5000/a/b" -> "http://localhost:5000/a/b"
const aTag = window.document.createElement("a");
Expand All @@ -522,6 +534,11 @@ export class HttpConnection implements IConnection {
}
negotiateUrl += "negotiate";
negotiateUrl += index === -1 ? "" : url.substring(index);

if (negotiateUrl.indexOf("negotiateVersion") === -1) {
negotiateUrl += index === -1 ? "?" : "&";
negotiateUrl += "negotiateVersion=" + this.negotiateVersion;
}
return negotiateUrl;
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/SignalR/clients/ts/signalr/tests/Common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export function eachTransport(action: (transport: HttpTransportType) => void) {

export function eachEndpointUrl(action: (givenUrl: string, expectedUrl: string) => void) {
const urls = [
[ "http://tempuri.org/endpoint/?q=my/Data", "http://tempuri.org/endpoint/negotiate?q=my/Data" ],
[ "http://tempuri.org/endpoint?q=my/Data", "http://tempuri.org/endpoint/negotiate?q=my/Data" ],
[ "http://tempuri.org/endpoint", "http://tempuri.org/endpoint/negotiate" ],
[ "http://tempuri.org/endpoint/", "http://tempuri.org/endpoint/negotiate" ],
[ "http://tempuri.org/endpoint/?q=my/Data", "http://tempuri.org/endpoint/negotiate?q=my/Data&negotiateVersion=1" ],
[ "http://tempuri.org/endpoint?q=my/Data", "http://tempuri.org/endpoint/negotiate?q=my/Data&negotiateVersion=1" ],
[ "http://tempuri.org/endpoint", "http://tempuri.org/endpoint/negotiate?negotiateVersion=1" ],
[ "http://tempuri.org/endpoint/", "http://tempuri.org/endpoint/negotiate?negotiateVersion=1" ],
];

urls.forEach((t) => action(t[0], t[1]));
Expand Down
Loading