From 34894b38ac95331cc2705cf6e887a82e84d05056 Mon Sep 17 00:00:00 2001 From: Sebastian Mayr Date: Sat, 3 Oct 2015 19:04:19 +0200 Subject: [PATCH] Implement loose mode for cookie jars --- README.md | 10 +++++----- lib/cookie.js | 21 +++++++++++++++++---- test/cookie_jar_test.js | 15 +++++++++++++++ 3 files changed, 37 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 46259c9d..9899dbf6 100644 --- a/README.md +++ b/README.md @@ -237,15 +237,15 @@ if (cookie.validate() === true) { Exported via `tough.CookieJar`. -### `CookieJar([store],[rejectPublicSuffixes])` +### `CookieJar([store],[options])` Simply use `new CookieJar()`. If you'd like to use a custom store, pass that to the constructor otherwise a `MemoryCookieStore` will be created and used. -### Properties - -CookieJar object properties: +The `options` object can be omitted and can have the following properties: - * _rejectPublicSuffixes_ - boolean - reject cookies with domains like "com" and "co.uk" (default: `true`) + * _rejectPublicSuffixes_ - boolean - default `true` - reject cookies with domains like "com" and "co.uk" + * _looseMode_ - boolean - default `false` - accept malformed cookies like `bar` and `=bar`, which have an implied empty name. + This is not in the standard, but is used sometimes on the web and is accepted by (most) browsers. Since eventually this module would like to support database/remote/etc. CookieJars, continuation passing style is used for CookieJar methods. diff --git a/lib/cookie.js b/lib/cookie.js index 26263c49..f1cb5e28 100644 --- a/lib/cookie.js +++ b/lib/cookie.js @@ -878,9 +878,17 @@ Cookie.prototype.canonicalizedDomain = function canonicalizedDomain() { return canonicalDomain(this.domain); }; -function CookieJar(store, rejectPublicSuffixes) { - if (rejectPublicSuffixes != null) { - this.rejectPublicSuffixes = rejectPublicSuffixes; +function CookieJar(store, options) { + if (typeof options === "boolean") { + options = {rejectPublicSuffixes: options}; + } else if (options == null) { + options = {}; + } + if (options.rejectPublicSuffixes != null) { + this.rejectPublicSuffixes = options.rejectPublicSuffixes; + } + if (options.looseMode != null) { + this.enableLooseMode = options.looseMode; } if (!store) { @@ -890,6 +898,7 @@ function CookieJar(store, rejectPublicSuffixes) { } CookieJar.prototype.store = null; CookieJar.prototype.rejectPublicSuffixes = true; +CookieJar.prototype.enableLooseMode = false; var CAN_BE_SYNC = []; CAN_BE_SYNC.push('setCookie'); @@ -902,10 +911,14 @@ CookieJar.prototype.setCookie = function(cookie, url, options, cb) { } var host = canonicalDomain(context.hostname); + var loose = this.enableLooseMode; + if (options.loose != null) { + loose = options.loose; + } // S5.3 step 1 if (!(cookie instanceof Cookie)) { - cookie = Cookie.parse(cookie, { loose: options.loose }); + cookie = Cookie.parse(cookie, { loose: loose }); } if (!cookie) { err = new Error("Cookie failed to parse"); diff --git a/test/cookie_jar_test.js b/test/cookie_jar_test.js index 689407b0..9d0691d5 100644 --- a/test/cookie_jar_test.js +++ b/test/cookie_jar_test.js @@ -465,4 +465,19 @@ vows } } }) + .addBatch({ + "Loose Mode": { + topic: function () { + var cj = new CookieJar(null, {looseMode: true}); + cj.setCookieSync("FooBar", 'http://www.foonet.net', {}); + return cj; + }, + "parses loose cookies": function (cj) { + var cookies = cj.getCookiesSync('http://www.foonet.net'); + assert.strictEqual(cookies.length, 1); + assert.strictEqual(cookies[0].key, ''); + assert.strictEqual(cookies[0].value, 'FooBar'); + } + } + }) .export(module);