Enable cookie jar for Node client using request package (#3202)

This commit is contained in:
BrennanConroy 2018-10-30 22:13:24 -07:00 committed by GitHub
parent 36ca210a65
commit 865de16086
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 838 additions and 132 deletions

View File

@ -103,6 +103,17 @@ namespace FunctionalTests
routes.MapConnectionHandler<EchoConnectionHandler>("/echo");
});
app.Use(async (context, next) =>
{
if (context.Request.Path.Value.Contains("/negotiate"))
{
context.Response.Cookies.Append("testCookie", "testValue");
context.Response.Cookies.Append("testCookie2", "testValue2");
context.Response.Cookies.Append("expiredCookie", "doesntmatter", new CookieOptions() { Expires = DateTimeOffset.Now.AddHours(-1) });
}
await next.Invoke();
});
app.UseSignalR(routes =>
{
routes.MapHub<TestHub>("/testhub");

View File

@ -88,5 +88,15 @@ namespace FunctionalTests
{
return Context.GetHttpContext().Request.Headers["Content-Type"];
}
public string GetCookie(string cookieName)
{
var cookies = Context.GetHttpContext().Request.Cookies;
if (cookies.TryGetValue(cookieName, out var cookieValue))
{
return cookieValue;
}
return null;
}
}
}

View File

@ -8,18 +8,123 @@
"version": "file:../signalr",
"requires": {
"eventsource": "^1.0.7",
"request": "^2.88.0",
"ws": "^6.0.0"
},
"dependencies": {
"@types/caseless": {
"version": "0.12.1",
"bundled": true
},
"@types/eventsource": {
"version": "1.0.2",
"bundled": true
},
"@types/form-data": {
"version": "2.2.1",
"bundled": true,
"requires": {
"@types/node": "*"
}
},
"@types/node": {
"version": "10.9.4",
"bundled": true
},
"@types/request": {
"version": "2.47.1",
"bundled": true,
"requires": {
"@types/caseless": "*",
"@types/form-data": "*",
"@types/node": "*",
"@types/tough-cookie": "*"
}
},
"@types/tough-cookie": {
"version": "2.3.3",
"bundled": true
},
"ajv": {
"version": "5.5.2",
"bundled": true,
"requires": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"asn1": {
"version": "0.2.4",
"bundled": true,
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"bundled": true
},
"async-limiter": {
"version": "1.0.0",
"bundled": true
},
"debug": {
"version": "2.6.9",
"asynckit": {
"version": "0.4.0",
"bundled": true
},
"aws-sign2": {
"version": "0.7.0",
"bundled": true
},
"aws4": {
"version": "1.8.0",
"bundled": true
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"bundled": true,
"requires": {
"ms": "2.0.0"
"tweetnacl": "^0.14.3"
}
},
"caseless": {
"version": "0.12.0",
"bundled": true
},
"co": {
"version": "4.6.0",
"bundled": true
},
"combined-stream": {
"version": "1.0.7",
"bundled": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"core-util-is": {
"version": "1.0.2",
"bundled": true
},
"dashdash": {
"version": "1.14.1",
"bundled": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"bundled": true
},
"ecc-jsbn": {
"version": "0.1.2",
"bundled": true,
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"es6-promise": {
@ -33,16 +138,110 @@
"original": "^1.0.0"
}
},
"extend": {
"version": "3.0.2",
"bundled": true
},
"extsprintf": {
"version": "1.3.0",
"bundled": true
},
"fast-deep-equal": {
"version": "1.1.0",
"bundled": true
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"bundled": true
},
"forever-agent": {
"version": "0.6.1",
"bundled": true
},
"form-data": {
"version": "2.3.3",
"bundled": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"getpass": {
"version": "0.1.7",
"bundled": true,
"requires": {
"assert-plus": "^1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"bundled": true
},
"har-validator": {
"version": "5.1.0",
"bundled": true,
"requires": {
"ajv": "^5.3.0",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"bundled": true,
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"is-typedarray": {
"version": "1.0.0",
"bundled": true
},
"ms": {
"version": "2.0.0",
"isstream": {
"version": "0.1.2",
"bundled": true
},
"nan": {
"version": "2.11.0",
"jsbn": {
"version": "0.1.1",
"bundled": true
},
"json-schema": {
"version": "0.2.3",
"bundled": true
},
"json-schema-traverse": {
"version": "0.3.1",
"bundled": true
},
"json-stringify-safe": {
"version": "5.0.1",
"bundled": true
},
"jsprim": {
"version": "1.4.1",
"bundled": true,
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
}
},
"mime-db": {
"version": "1.37.0",
"bundled": true
},
"mime-types": {
"version": "2.1.21",
"bundled": true,
"requires": {
"mime-db": "~1.37.0"
}
},
"oauth-sign": {
"version": "0.9.0",
"bundled": true
},
"original": {
@ -52,25 +251,98 @@
"url-parse": "^1.4.3"
}
},
"performance-now": {
"version": "2.1.0",
"bundled": true
},
"psl": {
"version": "1.1.29",
"bundled": true
},
"punycode": {
"version": "1.4.1",
"bundled": true
},
"qs": {
"version": "6.5.2",
"bundled": true
},
"querystringify": {
"version": "2.0.0",
"bundled": true
},
"request": {
"version": "2.88.0",
"bundled": true,
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.0",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.4.3",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"requires-port": {
"version": "1.0.0",
"bundled": true
},
"tslib": {
"version": "1.9.3",
"safe-buffer": {
"version": "5.1.2",
"bundled": true
},
"typedarray-to-buffer": {
"version": "3.1.5",
"safer-buffer": {
"version": "2.1.2",
"bundled": true
},
"sshpk": {
"version": "1.15.1",
"bundled": true,
"requires": {
"is-typedarray": "^1.0.0"
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"tough-cookie": {
"version": "2.4.3",
"bundled": true,
"requires": {
"psl": "^1.1.24",
"punycode": "^1.4.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"bundled": true,
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"bundled": true
},
"url-parse": {
"version": "1.4.3",
"bundled": true,
@ -79,14 +351,17 @@
"requires-port": "^1.0.0"
}
},
"websocket": {
"version": "1.0.26",
"uuid": {
"version": "3.3.2",
"bundled": true
},
"verror": {
"version": "1.10.0",
"bundled": true,
"requires": {
"debug": "^2.2.0",
"nan": "^2.3.3",
"typedarray-to-buffer": "^3.1.2",
"yaeti": "^0.0.6"
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"ws": {
@ -95,10 +370,6 @@
"requires": {
"async-limiter": "~1.0.0"
}
},
"yaeti": {
"version": "0.0.6",
"bundled": true
}
}
},
@ -200,10 +471,6 @@
"safe-buffer": "~5.1.0"
}
},
"tslib": {
"version": "1.9.3",
"bundled": true
},
"util-deprecate": {
"version": "1.0.2",
"bundled": true
@ -2819,9 +3086,9 @@
}
},
"nan": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz",
"integrity": "sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==",
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz",
"integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==",
"dev": true,
"optional": true
},

View File

@ -1,4 +1,4 @@
import { ChildProcess, execSync, spawn } from "child_process";
import { ChildProcess, exec, spawn } from "child_process";
import { EOL } from "os";
import { Readable } from "stream";
@ -163,26 +163,23 @@ function runJest(url: string) {
const configPath = path.resolve(__dirname, "..", "func.jest.config.js");
console.log("Starting Node tests using Jest.");
try {
execSync(`"${process.execPath}" "${jestPath}" --config "${configPath}"`, { env: { SERVER_URL: url }, timeout: 200000 });
return 0;
} catch (error) {
console.log(error.message);
console.log(error.stderr);
console.log(error.stdout);
return error.status || 1;
}
return new Promise<number>((resolve, reject) => {
const logStream = fs.createWriteStream(path.resolve(__dirname, "..", "..", "..", "..", "artifacts", "logs", "node.functionaltests.log"));
const p = exec(`"${process.execPath}" "${jestPath}" --config "${configPath}"`, { env: { SERVER_URL: url }, timeout: 200000, maxBuffer: 10 * 1024 * 1024 },
(error: any, stdout, stderr) => {
if (error) {
console.log(error.message);
return resolve(error.code);
}
return resolve(0);
});
p.stdout.pipe(logStream);
p.stderr.pipe(logStream);
});
}
(async () => {
try {
// Check if we got any browsers
if (config.browsers.length === 0) {
console.log("Unable to locate any suitable browsers. Skipping browser functional tests.");
process.exit(0);
return; // For good measure
}
const serverPath = path.resolve(__dirname, "..", "bin", configuration, "netcoreapp2.2", "FunctionalTests.dll");
debug(`Launching Functional Test Server: ${serverPath}`);
@ -243,14 +240,22 @@ function runJest(url: string) {
// Pass server URL to tests
conf.client.args = ["--server", url];
const results = await runKarma(conf);
const jestExit = await runJest(url);
const jestExit = runJest(url);
// Check if we got any browsers
let karmaExit;
if (config.browsers.length === 0) {
console.log("Unable to locate any suitable browsers. Skipping browser functional tests.");
} else {
karmaExit = (await runKarma(conf)).exitCode;
}
console.log(`karma exit code: ${results.exitCode}`);
if (karmaExit) {
console.log(`karma exit code: ${karmaExit}`);
}
console.log(`jest exit code: ${jestExit}`);
process.exit(results.exitCode !== 0 ? results.exitCode : jestExit);
process.exit(jestExit !== 0 ? jestExit : karmaExit);
} catch (e) {
console.error(e);
process.exit(1);

View File

@ -10,6 +10,7 @@ import { TestLogger } from "./TestLogger";
// We want to continue testing HttpConnection, but we don't export it anymore. So just pull it in directly from the source file.
import { HttpConnection } from "@aspnet/signalr/dist/esm/HttpConnection";
import "./LogBannerReporter";
const commonOptions: IHttpConnectionOptions = {
logMessageContent: true,

View File

@ -8,6 +8,7 @@ import { AbortError, DefaultHttpClient, HttpClient, HttpRequest, HttpResponse, H
import { MessagePackHubProtocol } from "@aspnet/signalr-protocol-msgpack";
import { eachTransport, eachTransportAndProtocol, ENDPOINT_BASE_URL } from "./Common";
import "./LogBannerReporter";
import { TestLogger } from "./TestLogger";
const TESTHUBENDPOINT_URL = ENDPOINT_BASE_URL + "/testhub";
@ -562,6 +563,26 @@ describe("hubConnection", () => {
});
});
}
it("preserves cookies between requests", async (done) => {
const hubConnection = getConnectionBuilder(transportType).build();
await hubConnection.start();
const cookieValue = await hubConnection.invoke<string>("GetCookie", "testCookie");
const cookieValue2 = await hubConnection.invoke<string>("GetCookie", "testCookie2");
expect(cookieValue).toEqual("testValue");
expect(cookieValue2).toEqual("testValue2");
await hubConnection.stop();
done();
});
it("expired cookies are not preserved", async (done) => {
const hubConnection = getConnectionBuilder(transportType).build();
await hubConnection.start();
const cookieValue = await hubConnection.invoke<string>("GetCookie", "expiredCookie");
expect(cookieValue).toBeNull();
await hubConnection.stop();
done();
});
});
});

View File

@ -16,4 +16,9 @@ export class LogBannerReporter implements jasmine.CustomReporter {
}
}
jasmine.getEnv().addReporter(new LogBannerReporter());
if (typeof window !== "undefined" && (window as any).customReporterRegistered !== true) {
(window as any).customReporterRegistered = true;
jasmine.getEnv().addReporter(new LogBannerReporter());
} else if (typeof window === "undefined") {
jasmine.getEnv().addReporter(new LogBannerReporter());
}

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
import { ECHOENDPOINT_URL } from "./Common";
import "./LogBannerReporter";
// On slower CI machines, these tests sometimes take longer than 5s
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10 * 1000;

View File

@ -37,5 +37,6 @@ module.exports = {
externals: {
"@aspnet/signalr": "signalR",
"@aspnet/signalr-protocol-msgpack": "signalR.protocols.msgpack",
"request": "request",
},
};

View File

@ -1,6 +1,6 @@
{
"name": "@aspnet/signalr-protocol-msgpack",
"version": "1.1.0-preview3-t000",
"version": "1.1.0-rtm-t000",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "@aspnet/signalr-protocol-msgpack",
"version": "1.1.0-preview1-t000",
"version": "1.1.0-rtm-t000",
"description": "MsgPack Protocol support for ASP.NET Core SignalR",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",

View File

@ -1,26 +1,151 @@
{
"name": "@aspnet/signalr",
"version": "1.1.0-preview3-t000",
"version": "1.1.0-rtm-t000",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@types/caseless": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.1.tgz",
"integrity": "sha512-FhlMa34NHp9K5MY1Uz8yb+ZvuX0pnvn3jScRSNAb75KHGB8d3rEU6hqMs3Z2vjuytcMfRg6c5CHMc3wtYyD2/A==",
"dev": true
},
"@types/eventsource": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/eventsource/-/eventsource-1.0.2.tgz",
"integrity": "sha512-CprOekOB/lzAiGDF1MPWHX053RVTCYyYU3M8HOQXpdD0QfXijM//Na/hZxHaQv4ydsiB1uOBQ3p8S5nXpP4nNQ==",
"dev": true
},
"@types/form-data": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-2.2.1.tgz",
"integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/node": {
"version": "10.9.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.9.4.tgz",
"integrity": "sha512-fCHV45gS+m3hH17zgkgADUSi2RR1Vht6wOZ0jyHP8rjiQra9f+mIcgwPQHllmDocYOstIEbKlxbFDYlgrTPYqw==",
"dev": true
},
"@types/request": {
"version": "2.47.1",
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.47.1.tgz",
"integrity": "sha512-TV3XLvDjQbIeVxJ1Z3oCTDk/KuYwwcNKVwz2YaT0F5u86Prgc4syDAp6P96rkTQQ4bIdh+VswQIC9zS6NjY7/g==",
"dev": true,
"requires": {
"@types/caseless": "*",
"@types/form-data": "*",
"@types/node": "*",
"@types/tough-cookie": "*"
}
},
"@types/tough-cookie": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-2.3.3.tgz",
"integrity": "sha512-MDQLxNFRLasqS4UlkWMSACMKeSm1x4Q3TxzUC7KQUsh6RK1ZrQ0VEyE3yzXcBu+K8ejVj4wuX32eUG02yNp+YQ==",
"dev": true
},
"ajv": {
"version": "5.5.2",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz",
"integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=",
"requires": {
"co": "^4.6.0",
"fast-deep-equal": "^1.0.0",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.3.0"
}
},
"asn1": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
"requires": {
"safer-buffer": "~2.1.0"
}
},
"assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"async-limiter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg=="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
},
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
},
"aws4": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
"integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ=="
},
"bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
"requires": {
"tweetnacl": "^0.14.3"
}
},
"caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
},
"co": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
},
"combined-stream": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
"integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
"requires": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"es6-promise": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.2.tgz",
@ -35,6 +160,132 @@
"original": "^1.0.0"
}
},
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
},
"fast-deep-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
"integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ="
},
"fast-json-stable-stringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
},
"forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
},
"form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
"requires": {
"assert-plus": "^1.0.0"
}
},
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
},
"har-validator": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz",
"integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==",
"requires": {
"ajv": "^5.3.0",
"har-schema": "^2.0.0"
}
},
"http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
"requires": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
}
},
"is-typedarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
},
"isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"json-schema": {
"version": "0.2.3",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
},
"json-schema-traverse": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
"integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
},
"json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"jsprim": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
"requires": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.2.3",
"verror": "1.10.0"
}
},
"mime-db": {
"version": "1.37.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz",
"integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg=="
},
"mime-types": {
"version": "2.1.21",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz",
"integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==",
"requires": {
"mime-db": "~1.37.0"
}
},
"oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
},
"original": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
@ -43,16 +294,111 @@
"url-parse": "^1.4.3"
}
},
"performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"psl": {
"version": "1.1.29",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz",
"integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ=="
},
"punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
},
"qs": {
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
},
"querystringify": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.0.0.tgz",
"integrity": "sha512-eTPo5t/4bgaMNZxyjWx6N2a6AuE0mq51KWvpc7nU/MAqixcI6v6KrGUKES0HaomdnolQBBXU/++X6/QQ9KL4tw=="
},
"request": {
"version": "2.88.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
"requires": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.0",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.4.3",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
}
},
"requires-port": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sshpk": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.15.1.tgz",
"integrity": "sha512-mSdgNUaidk+dRU5MhYtN9zebdzF2iG0cNPWy8HG+W8y+fT1JnSkh0fzzpjOa0L7P8i1Rscz38t0h4gPcKz43xA==",
"requires": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
}
},
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
"requires": {
"psl": "^1.1.24",
"punycode": "^1.4.1"
}
},
"tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"requires": {
"safe-buffer": "^5.0.1"
}
},
"tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
},
"url-parse": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz",
@ -62,6 +408,21 @@
"requires-port": "^1.0.0"
}
},
"uuid": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
"integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA=="
},
"verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
"requires": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"ws": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.0.0.tgz",

View File

@ -1,6 +1,6 @@
{
"name": "@aspnet/signalr",
"version": "1.1.0-preview1-t000",
"version": "1.1.0-rtm-t000",
"description": "ASP.NET Core SignalR Client",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
@ -38,10 +38,12 @@
"devDependencies": {
"es6-promise": "^4.2.2",
"@types/node": "^10.9.4",
"@types/eventsource": "^1.0.2"
"@types/eventsource": "^1.0.2",
"@types/request": "^2.47.1"
},
"dependencies": {
"ws": "^6.0.0",
"eventsource": "^1.0.7"
"eventsource": "^1.0.7",
"request": "^2.88.0"
}
}

View File

@ -45,4 +45,8 @@ export class DefaultHttpClient extends HttpClient {
return this.httpClient.send(request);
}
public getCookieString(url: string): string {
return this.httpClient.getCookieString(url);
}
}

View File

@ -141,4 +141,14 @@ export abstract class HttpClient {
* @returns {Promise<HttpResponse>} A Promise that resolves with an HttpResponse describing the response, or rejects with an Error indicating a failure.
*/
public abstract send(request: HttpRequest): Promise<HttpResponse>;
/** Gets all cookies that apply to the specified URL.
*
* @param url The URL that the cookies are valid for.
* @returns {string} A string containing all the key-value cookie pairs for the specified URL.
*/
// @ts-ignore
public getCookieString(url: string): string {
return "";
}
}

View File

@ -299,7 +299,7 @@ export class HttpConnection implements IConnection {
if (!this.options.WebSocket) {
throw new Error("'WebSocket' is not supported in your environment.");
}
return new WebSocketTransport(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);
case HttpTransportType.ServerSentEvents:
if (!this.options.EventSource) {
throw new Error("'EventSource' is not supported in your environment.");

View File

@ -1,9 +1,7 @@
// 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.
import * as http from "http";
import * as https from "https";
import { URL } from "url";
import * as Request from "request";
import { AbortError, HttpError, TimeoutError } from "./Errors";
import { HttpClient, HttpRequest, HttpResponse } from "./HttpClient";
@ -12,87 +10,70 @@ import { isArrayBuffer } from "./Utils";
export class NodeHttpClient extends HttpClient {
private readonly logger: ILogger;
private readonly request: Request.RequestAPI<Request.Request, Request.CoreOptions, Request.RequiredUriUrl>;
private readonly cookieJar: Request.CookieJar;
public constructor(logger: ILogger) {
super();
this.logger = logger;
this.cookieJar = Request.jar();
this.request = Request.defaults({ jar: this.cookieJar });
}
public send(request: HttpRequest): Promise<HttpResponse> {
public send(httpRequest: HttpRequest): Promise<HttpResponse> {
return new Promise<HttpResponse>((resolve, reject) => {
const url = new URL(request.url!);
const options: http.RequestOptions = {
let requestBody: Buffer | string;
if (isArrayBuffer(httpRequest.content)) {
requestBody = Buffer.from(httpRequest.content);
} else {
requestBody = httpRequest.content || "";
}
const currentRequest = this.request(httpRequest.url!, {
body: requestBody,
// If binary is expected 'null' should be used, otherwise for text 'utf8'
encoding: httpRequest.responseType === "arraybuffer" ? null : "utf8",
headers: {
// Tell auth middleware to 401 instead of redirecting
"X-Requested-With": "XMLHttpRequest",
...request.headers,
...httpRequest.headers,
},
hostname: url.hostname,
method: request.method,
// /abc/xyz + ?id=12ssa_30
path: url.pathname + url.search,
port: url.port,
};
method: httpRequest.method,
timeout: httpRequest.timeout,
},
(error, response, body) => {
if (httpRequest.abortSignal) {
httpRequest.abortSignal.onabort = null;
}
// "any" is used here because require() can't be correctly resolved by the compiler
// when httpOrHttps is typeof http | typeof https. Change to http when editing to get
// intellisense.
const httpOrHttps: any = url.protocol === "https" ? https : http;
const req = httpOrHttps.request(options, (res: http.IncomingMessage) => {
const data: Buffer[] = [];
let dataLength = 0;
res.on("data", (chunk: any) => {
data.push(chunk);
// Buffer.concat will be slightly faster if we keep track of the length
dataLength += chunk.length;
});
res.on("end", () => {
if (request.abortSignal) {
request.abortSignal.onabort = null;
if (error) {
if (error.code === "ETIMEDOUT") {
this.logger.log(LogLevel.Warning, `Timeout from HTTP request.`);
reject(new TimeoutError());
}
this.logger.log(LogLevel.Warning, `Error from HTTP request. ${error}`);
reject(error);
return;
}
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
let resp: string | ArrayBuffer;
if (request.responseType === "arraybuffer") {
resp = Buffer.concat(data, dataLength);
resolve(new HttpResponse(res.statusCode, res.statusMessage || "", resp));
} else {
resp = Buffer.concat(data, dataLength).toString();
resolve(new HttpResponse(res.statusCode, res.statusMessage || "", resp));
}
} else {
reject(new HttpError(res.statusMessage || "", res.statusCode || 0));
}
});
if (response.statusCode >= 200 && response.statusCode < 300) {
resolve(new HttpResponse(response.statusCode, response.statusMessage || "", body));
} else {
reject(new HttpError(response.statusMessage || "", response.statusCode || 0));
}
});
if (request.abortSignal) {
request.abortSignal.onabort = () => {
req.abort();
if (httpRequest.abortSignal) {
httpRequest.abortSignal.onabort = () => {
currentRequest.abort();
reject(new AbortError());
};
}
if (request.timeout) {
req.setTimeout(request.timeout, () => {
this.logger.log(LogLevel.Warning, `Timeout from HTTP request.`);
reject(new TimeoutError());
});
}
req.on("error", (e: Error) => {
this.logger.log(LogLevel.Warning, `Error from HTTP request. ${e}`);
reject(e);
});
if (isArrayBuffer(request.content)) {
req.write(Buffer.from(request.content));
} else {
req.write(request.content || "");
}
req.end();
});
}
public getCookieString(url: string): string {
return this.cookieJar.getCookieString(url);
}
}

View File

@ -8,7 +8,7 @@ export interface EventSourceConstructor {
}
export interface WebSocketConstructor {
new(url: string, protocols?: string | string[]): WebSocket;
new(url: string, protocols?: string | string[], options?: any): WebSocket;
readonly CLOSED: number;
readonly CLOSING: number;
readonly CONNECTING: number;

View File

@ -56,7 +56,14 @@ export class ServerSentEventsTransport implements ITransport {
return;
}
const eventSource = new this.eventSourceConstructor(url, { withCredentials: true });
let eventSource: EventSource;
if (typeof window !== "undefined") {
eventSource = new this.eventSourceConstructor(url, { withCredentials: true });
} else {
// Non-browser passes cookies via the dictionary
const cookies = this.httpClient.getCookieString(url);
eventSource = new this.eventSourceConstructor(url, { withCredentials: true, headers: { Cookie: cookies } } as EventSourceInit);
}
try {
eventSource.onmessage = (e: MessageEvent) => {

View File

@ -1,6 +1,7 @@
// 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.
import { HttpClient } from "./HttpClient";
import { ILogger, LogLevel } from "./ILogger";
import { ITransport, TransferFormat } from "./ITransport";
import { WebSocketConstructor } from "./Polyfills";
@ -12,17 +13,19 @@ export class WebSocketTransport implements ITransport {
private readonly accessTokenFactory: (() => string | Promise<string>) | undefined;
private readonly logMessageContent: boolean;
private readonly webSocketConstructor: WebSocketConstructor;
private readonly httpClient: HttpClient;
private webSocket?: WebSocket;
public onreceive: ((data: string | ArrayBuffer) => void) | null;
public onclose: ((error?: Error) => void) | null;
constructor(accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger,
constructor(httpClient: HttpClient, accessTokenFactory: (() => string | Promise<string>) | undefined, logger: ILogger,
logMessageContent: boolean, webSocketConstructor: WebSocketConstructor) {
this.logger = logger;
this.accessTokenFactory = accessTokenFactory;
this.logMessageContent = logMessageContent;
this.webSocketConstructor = webSocketConstructor;
this.httpClient = httpClient;
this.onreceive = null;
this.onclose = null;
@ -44,7 +47,23 @@ export class WebSocketTransport implements ITransport {
return new Promise<void>((resolve, reject) => {
url = url.replace(/^http/, "ws");
const webSocket = new this.webSocketConstructor(url);
let webSocket: WebSocket | undefined;
const cookies = this.httpClient.getCookieString(url);
if (typeof window === "undefined" && cookies) {
// Only pass cookies when in non-browser environments
webSocket = new this.webSocketConstructor(url, undefined, {
headers: {
Cookie: `${cookies}`,
},
});
}
if (!webSocket) {
// Chrome is not happy with passing 'undefined' as protocol
webSocket = new this.webSocketConstructor(url);
}
if (transferFormat === TransferFormat.Binary) {
webSocket.binaryType = "arraybuffer";
}

View File

@ -6,6 +6,7 @@ import { TransferFormat } from "../src/ITransport";
import { WebSocketTransport } from "../src/WebSocketTransport";
import { VerifyLogger } from "./Common";
import { TestMessageEvent } from "./TestEventSource";
import { TestHttpClient } from "./TestHttpClient";
import { TestCloseEvent, TestErrorEvent, TestEvent, TestWebSocket } from "./TestWebSocket";
import { registerUnhandledRejectionHandler } from "./Utils";
@ -22,7 +23,7 @@ describe("WebSocketTransport", () => {
it("connect waits for WebSocket to be connected", async () => {
await VerifyLogger.run(async (logger) => {
const webSocket = new WebSocketTransport(undefined, logger, true, TestWebSocket);
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket);
let connectComplete: boolean = false;
const connectPromise = (async () => {
@ -44,7 +45,7 @@ describe("WebSocketTransport", () => {
it("connect fails if there is error during connect", async () => {
await VerifyLogger.run(async (logger) => {
(global as any).ErrorEvent = TestErrorEvent;
const webSocket = new WebSocketTransport(undefined, logger, true, TestWebSocket);
const webSocket = new WebSocketTransport(new TestHttpClient(), undefined, logger, true, TestWebSocket);
let connectComplete: boolean = false;
const connectPromise = (async () => {
@ -204,7 +205,7 @@ describe("WebSocketTransport", () => {
});
async function createAndStartWebSocket(logger: ILogger, url?: string, accessTokenFactory?: (() => string | Promise<string>), format?: TransferFormat): Promise<WebSocketTransport> {
const webSocket = new WebSocketTransport(accessTokenFactory, logger, true, TestWebSocket);
const webSocket = new WebSocketTransport(new TestHttpClient(), accessTokenFactory, logger, true, TestWebSocket);
const connectPromise = webSocket.connect(url || "http://example.com", format || TransferFormat.Text);

View File

@ -8,7 +8,6 @@ module.exports = baseConfig(__dirname, "signalr", {
externals: [
"websocket",
"eventsource",
"http",
"url",
"request"
]
});