RFC6265 用于 Node.js 的 Cookie 与 CookieJar
var tough = require('tough-cookie'); var Cookie = tough.Cookie; var cookie = Cookie.parse(header); cookie.value = 'somethingdifferent'; header = cookie.toString();
var cookiejar = new tough.CookieJar(); cookiejar.setCookie(cookie, 'http://currentdomain.example.com/path', cb); // ... cookiejar.getCookies('http://example.com/otherpath',function(err,cookies) { res.headers['cookie'] = cookies.join('; '); });
# 安装
这 _太_ 容易了!
`npm install tough-cookie`
为什么叫这个名字? NPM 模块 `cookie`、`cookies` 和 `cookiejar` 都已被占用。
## 版本支持
对 node.js 版本的支持将遵循 [request](https://www.npmjs.com/package/request) 模块的支持策略。
# API
## tough
`require('tough-cookie')` 返回的模块上的函数。 所有这些函数都可以作为纯函数使用,无需"绑定"。
**注意**:在 1.0.x 之前,其中几个函数接受 `strict` 参数。由于该参数已不再必要,因此已从 API 中移除。
### `parseDate(string)`
将 cookie 日期字符串解析为 `Date` 对象。 解析遵循 RFC6265 第 5.1.1 节,而不是 `Date.parse()`。
### `formatDate(date)`
将 Date 格式化为 RFC1123 字符串(RFC6265 推荐的格式)。
### `canonicalDomain(str)`
将域名转换为规范域名。 规范域名是去除首尾空白、转换为小写、去除前导点号并可选用 punycode 编码的域名(RFC6265 第 5.1.2 节)。 在大多数情况下,此函数是幂等的(可以对其输出再次运行而不会产生不良影响)。
### `domainMatch(str,domStr[,canonicalize=true])`
回答"这个真实域名是否匹配 cookie 中的域名?"。 `str` 是"当前"域名,`domStr` 是"cookie"域名。 匹配遵循 RFC6265 第 5.1.3 节,但可以把它理解为"后缀匹配"。
`canonicalize` 参数决定是否将另外两个参数交给 `canonicalDomain` 处理。
### `defaultPath(path)`
给定当前的请求/响应路径,返回适合存储在 cookie 中的 Path。 这基本上就是路径中"文件"的"目录",但这由 RFC 第 5.1.4 节规定。
`path` 参数必须 _只是_ URI 的路径名部分(即排除主机名、查询、片段等)。 这是 node 的 `uri.parse()` 输出中的 `.pathname` 属性。
### `pathMatch(reqPath,cookiePath)`
按照 RFC6265 第 5.1.4 节,回答"请求路径是否路径匹配给定的 cookie 路径?"。 返回布尔值。
这本质上是一种前缀匹配,其中 `cookiePath` 是 `reqPath` 的前缀。
### `parse(cookieString[, options])`
`Cookie.parse(cookieString[, options])` 的别名
### `fromJSON(string)`
`Cookie.fromJSON(string)` 的别名
### `getPublicSuffix(hostname)`
返回此主机名的公共后缀。 公共后缀是可以设置 cookie 的最短域名。 如果无法为该主机名设置 cookie,则返回 `null`。
例如:`www.example.com` 和 `www.subdomain.example.com` 的公共后缀都是 `example.com`。
更多信息,请参阅 http://publicsuffix.org/。 此模块的列表源自该网站。此调用目前是对 [`psl`](https://www.npmjs.com/package/psl) 的 [get() 方法](https://www.npmjs.com/package/psl#pslgetdomain) 的封装。
### `cookieCompare(a,b)`
与 `.sort()` 配合使用,将 cookie 列表按 RFC 建议的顺序(第 5.4 节第 2 步)排序。排序算法按优先级顺序如下:
* `.path` 最长
* `.creation` 最早(精度为 1ms,与 `Date` 相同)
* `.creationIndex` 最小(以突破 1ms 精度限制)``` javascript
var cookies = [ /* unsorted array of Cookie objects */ ];
cookies = cookies.sort(cookieCompare);
注意:由于 JavaScript 的 Date 精度被限制为 1 毫秒,因此同一个毫秒内完全可能出现多个 cookie。当使用 .setCookie() 的 now 选项时尤其如此。.creationIndex 属性是一个进程级全局计数器,在通过 new Cookie() 构造时分配。这保留了 RFC 排序的精神:较早的 cookie 排在前面。这对于 MemoryCookieStore 来说效果很好,因为 Set-Cookie 头是按顺序解析的,但对于分布式系统可能就不那么好了。精密的 Store 可能希望将此设置为某种其他的 逻辑时钟,以便如果 cookie A 和 B 在同一毫秒内创建,但 cookie A 先于 cookie B 创建,则 A.creationIndex < B.creationIndex。如果你想更改全局计数器(你大概 不应该 这么做),它存储在 Cookie.cookiesCreated 中。
permuteDomain(domain)生成一个列表,包含所有可能通过 domainMatch() 匹配该参数的域。对于实现 cookie 存储可能很有用。
permutePath(path)生成一个列表,包含所有可能通过 pathMatch() 匹配该参数的路径。对于实现 cookie 存储可能很有用。
通过 tough.Cookie 导出。
Cookie.parse(cookieString[, options])将单个 Cookie 或 Set-Cookie HTTP 头解析为 Cookie 对象。如果字符串无法解析,则返回 undefined。
options 参数不是必需的,目前只有一个属性:
true,则启用对无键 cookie 的解析(例如 =abc 和 =),这些 cookie 不符合 RFC 规范。如果 options 不是对象,则会被忽略,这意味着你可以将其与 Array#map 一起使用。
以下是在 node HTTP/HTTPS 响应中处理 Set-Cookie 头的方法:``` javascript if (res.headers['set-cookie'] instanceof Array) cookies = res.headers['set-cookie'].map(Cookie.parse); else cookies = [Cookie.parse(res.headers['set-cookie'])];
_注意:_ 在 2.3.3 版本中,tough-cookie 限制了 `=` 之前的空格数为 256 个字符。此限制之后已被移除。
请参阅 [Issue 92](https://github.com/salesforce/tough-cookie/issues/92)
### 属性
Cookie 对象的属性:
* _key_ - 字符串 - cookie 的名称或键(默认值为 "")
* _value_ - 字符串 - cookie 的值(默认值为 "")
* _expires_ - `Date` - 若设置,则为 cookie 的 `Expires=` 属性(默认字符串为 `"Infinity"`)。参见 `setExpires()`
* _maxAge_ - 秒 - 若设置,则为 cookie 的 `Max-Age=` 属性(以_秒_为单位)。也可分别设置为字符串 `"Infinity"` 和 `"-Infinity"`,表示永不过期和立即过期。参见 `setMaxAge()`
* _domain_ - 字符串 - cookie 的 `Domain=` 属性
* _path_ - 字符串 - cookie 的 `Path=` 属性
* _secure_ - 布尔值 - `Secure` cookie 标志
* _httpOnly_ - 布尔值 - `HttpOnly` cookie 标志
* _extensions_ - `Array` - 任何无法识别的 cookie 属性,以字符串形式表示(即使其中包含等号)
* _creation_ - `Date` - 此 cookie 被构造的时间
* _creationIndex_ - 数值 - 在构造时设置,用于提供更高的排序精度(完整说明请参阅 `cookieCompare(a,b)`)
在 cookie 经过 `CookieJar.setCookie()` 处理后,它还将具有以下附加属性:
* _hostOnly_ - 布尔值 - 是否为仅主机(host-only)cookie(即未设置 Domain 字段,而是隐含存在)
* _pathIsDefault_ - 布尔值 - 若为 true,则表示 cookie 上没有 Path 字段,并且使用 `defaultPath()` 推导出了该字段。
* _creation_ - `Date` - 从构造时间**修改**为 cookie 被添加到 jar 的时间
* _lastAccessed_ - `Date` - cookie 最后一次被访问的时间。将来实现时会用于 cookie 清理。使用 `cookiejar.getCookies(...)` 将更新此属性。
### `Cookie([{properties}])`
接收一个选项对象,该对象可以包含上述任何 Cookie 属性;未指定的属性将使用默认值。
### `.toString()`
编码为 Set-Cookie 头部的值。Expires cookie 字段使用 `formatDate()` 设置,但如果 `.expires` 为 `Infinity`,则完全省略该字段。
### `.cookieString()`
编码为 Cookie 头部的值(即 `.key` 和 `.value` 属性以 '=' 连接)。
### `.setExpires(String)`
根据通过 `parseDate()` 传入的日期字符串设置过期时间。如果 parseDate 返回 `null`(即无法解析该日期字符串),则 `.expires` 会被设置为字符串 `"Infinity"`。
### `.setMaxAge(number)`
以秒为单位设置 maxAge。将 `-Infinity` 强制转换为 `"-Infinity"`,将 `Infinity` 强制转换为 `"Infinity"`,以便 JSON 序列化正确。
### `.expiryTime([now=Date.now()])`
### `.expiryDate([now=Date.now()])`
expiryTime() 计算此 cookie 过期的绝对 Unix 纪元毫秒数。expiryDate() 的工作方式类似,区别在于它返回一个 `Date` 对象。请注意,在这两种情况下,`now` 参数都应为毫秒。
Max-Age 优先于 Expires(按照 RFC 规定)。`.creation` 属性 -- 或默认情况下的 `now` 参数 -- 用于偏移 `.maxAge` 属性。
如果设置了 Expires(`.expires`),则返回该值。
否则,`expiryTime()` 返回 `Infinity`,`expiryDate()` 返回 "Tue, 19 Jan 2038 03:14:07 GMT" 对应的 `Date` 对象(这是 32 位 `time_t` 能表示的最新日期;也是大多数用户代理的常见上限)。
### `.TTL([now=Date.now()])`
计算相对于 `now`(毫秒)的 TTL。与 `expiryTime`/`expiryDate` 相同的优先级规则生效。
对于没有显式过期时间的 cookie,返回数值 `Infinity`;如果 cookie 已过期,则返回 `0`。否则返回以毫秒为单位的生存时间。
### `.canonicalizedDomain()`
### `.cdomain()`
返回规范化后的 `.domain` 字段。如果域包含任何非 ASCII 字符,则会进行小写化并进行 punycode(RFC3490)编码。
### `.toJSON()`
为了便于使用 `JSON.serialize(cookie)`,返回一个可被 JSON 序列化的普通 `Object`。
所有 `Date` 属性(即 `.expires`、`.creation` 和 `.lastAccessed`)均以 ISO 格式(`.toISOString()`)导出。
**注意**:自定义的 `Cookie` 属性将被丢弃。在 tough-cookie 1.x 中,由于没有显式定义 `.toJSON` 方法,所有可枚举属性都会被捕获。如果你希望某个属性被序列化,请将该属性名称添加到 `Cookie.serializableProperties` 数组中。
### `Cookie.fromJSON(strOrObj)`
与 `cookie.toJSON()` 相反。如果传入的是字符串,将先对其进行 `JSON.parse()`。
所有 `Date` 属性(即 `.expires`、`.creation` 和 `.lastAccessed`)都通过 `Date.parse()` 解析,而不是 tough-cookie 的 `parseDate`,因为在当前这一层处理的是 JavaScript/JSON 风格的时间戳。
如果 JSON 解析出错,则返回 `null`。
### `.clone()`
深克隆此 cookie,实现方式完全等同于 `Cookie.fromJSON(cookie.toJSON())`。
### `.validate()`
状态:*进行中*。可用于部分场景,但远非全面。
验证 cookie 属性在语义上的正确性。可用于对生成的任何 Set-Cookie 头部进行 "lint" 检查。目前它返回布尔值,但最终可能会返回原因字符串——你可以使用以下构造来为未来做准备:``` javascript
if (cookie.validate() === true) {
// it's tasty
} else {
// yuck!
}
通过 tough.CookieJar 导出。
CookieJar([store],[options])只需使用 new CookieJar()。 如果您希望使用自定义存储,请将其传递给构造函数,否则将创建并使用 MemoryCookieStore。
options 对象可以省略,并且可以具有以下属性:
true - 拒绝域名类似 "com" 和 "co.uk" 的 Cookiefalse - 接受像 bar 和 =bar 这样的畸形 Cookie,它们隐含一个空名称。
这不符合标准,但在网络上有时会用到,并且(大多数)浏览器会接受。由于该模块最终希望支持数据库/远程等 CookieJar,因此 CookieJar 方法采用延续传递风格。
.setCookie(cookieOrString, currentUrl, [{options},] cb(err,cookie))尝试在 Cookie jar 中设置 Cookie。如果操作失败,错误将传递给回调 cb,否则该 Cookie 会被传递出去。Cookie 的 .creation、.lastAccessed 和 .hostOnly 属性将得到更新。
options 对象可以省略,并且可以具有以下属性: